diff options
author | John Reck <jreck@google.com> | 2012-05-02 16:22:23 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2012-05-02 16:22:23 -0700 |
commit | 2230ea9b1f51c8c57087e5509d79500bef57a3ea (patch) | |
tree | e38e3f95c19f0558cf277e7d3078017f5a964370 | |
parent | d40f1d5823415a70cd35d224d16cf3395c5e52f7 (diff) | |
parent | e859a34171f2a36877d95197d118d962078f8aa0 (diff) | |
download | external_webkit-2230ea9b1f51c8c57087e5509d79500bef57a3ea.zip external_webkit-2230ea9b1f51c8c57087e5509d79500bef57a3ea.tar.gz external_webkit-2230ea9b1f51c8c57087e5509d79500bef57a3ea.tar.bz2 |
am e859a341: Rewrite PictureSet with TURBO!
* commit 'e859a34171f2a36877d95197d118d962078f8aa0':
Rewrite PictureSet with TURBO!
42 files changed, 850 insertions, 1877 deletions
diff --git a/Source/WebCore/Android.mk b/Source/WebCore/Android.mk index 794a4a8..742fade 100644 --- a/Source/WebCore/Android.mk +++ b/Source/WebCore/Android.mk @@ -670,7 +670,7 @@ LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \ platform/graphics/android/layers/MediaLayer.cpp \ platform/graphics/android/layers/MediaTexture.cpp \ platform/graphics/android/layers/PictureLayerContent.cpp \ - platform/graphics/android/layers/PictureSetLayerContent.cpp \ + platform/graphics/android/layers/PicturePileLayerContent.cpp \ platform/graphics/android/layers/ScrollableLayerAndroid.cpp \ platform/graphics/android/layers/VideoLayerAndroid.cpp \ platform/graphics/android/layers/VideoLayerManager.cpp \ diff --git a/Source/WebCore/platform/android/ScrollViewAndroid.cpp b/Source/WebCore/platform/android/ScrollViewAndroid.cpp index ecaa2b5..cc1c09e 100644 --- a/Source/WebCore/platform/android/ScrollViewAndroid.cpp +++ b/Source/WebCore/platform/android/ScrollViewAndroid.cpp @@ -103,13 +103,7 @@ void ScrollView::platformOffscreenContentRectangle(const IntRect& vis, const Int android::WebViewCore* core = android::WebViewCore::getWebViewCore(this); if (!core) // SVG does not instantiate webviewcore return; // and doesn't need to record drawing offscreen - SkRegion rectRgn = SkRegion(rect); - rectRgn.op(vis, SkRegion::kDifference_Op); - SkRegion::Iterator iter(rectRgn); - for (; !iter.done(); iter.next()) { - const SkIRect& diff = iter.rect(); - core->offInvalidate(diff); - } + core->offInvalidate(rect); } #endif diff --git a/Source/WebCore/platform/android/WidgetAndroid.cpp b/Source/WebCore/platform/android/WidgetAndroid.cpp index 0f7758d..6858f29 100644 --- a/Source/WebCore/platform/android/WidgetAndroid.cpp +++ b/Source/WebCore/platform/android/WidgetAndroid.cpp @@ -59,10 +59,6 @@ void Widget::setFocus(bool focused) void Widget::paint(GraphicsContext* ctx, const IntRect& r) { - // FIXME: in what case, will this be called for the top frame? - if (!platformWidget()) - return; - platformWidget()->draw(ctx, r); } void Widget::releasePlatformWidget() diff --git a/Source/WebCore/platform/graphics/android/layers/LayerAndroid.cpp b/Source/WebCore/platform/graphics/android/layers/LayerAndroid.cpp index 69c597f..535d211 100644 --- a/Source/WebCore/platform/graphics/android/layers/LayerAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/layers/LayerAndroid.cpp @@ -20,6 +20,7 @@ #include "MediaLayer.h" #include "ParseCanvas.h" #include "PictureLayerContent.h" +#include "PrerenderedInval.h" #include "SkBitmapRef.h" #include "SkDrawFilter.h" #include "SkPaint.h" @@ -524,6 +525,20 @@ void LayerAndroid::setContent(LayerContent* content) m_content = content; } +bool LayerAndroid::canUpdateWithBlit() +{ + if (!m_content || !m_scale) + return false; + PrerenderedInval* prerendered = m_content->prerenderForRect(m_dirtyRegion.getBounds()); + if (!prerendered) + return false; + // Check that the scales are "close enough" to produce the same rects + FloatRect screenArea = prerendered->screenArea; + screenArea.scale(1 / m_scale); + IntRect enclosingDocArea = enclosingIntRect(screenArea); + return enclosingDocArea == prerendered->area; +} + bool LayerAndroid::needsTexture() { return m_content && !m_content->isEmpty(); diff --git a/Source/WebCore/platform/graphics/android/layers/LayerAndroid.h b/Source/WebCore/platform/graphics/android/layers/LayerAndroid.h index 4f94698..ca833f7 100644 --- a/Source/WebCore/platform/graphics/android/layers/LayerAndroid.h +++ b/Source/WebCore/platform/graphics/android/layers/LayerAndroid.h @@ -185,6 +185,9 @@ public: LayerContent* content() { return m_content; } void setContent(LayerContent* content); + // Check to see if the dirty area of this layer can be updated with a blit + // from the prerender instead of needing to generate tiles from the LayerContent + bool canUpdateWithBlit(); void addAnimation(PassRefPtr<AndroidAnimation> anim); void removeAnimationsForProperty(AnimatedPropertyID property); diff --git a/Source/WebCore/platform/graphics/android/layers/LayerContent.h b/Source/WebCore/platform/graphics/android/layers/LayerContent.h index 97bc32a..2cd90a90 100644 --- a/Source/WebCore/platform/graphics/android/layers/LayerContent.h +++ b/Source/WebCore/platform/graphics/android/layers/LayerContent.h @@ -26,6 +26,7 @@ #ifndef LayerContent_h #define LayerContent_h +#include "IntRect.h" #include "SkRefCnt.h" #include <utils/threads.h> @@ -35,14 +36,18 @@ class SkWStream; namespace WebCore { +class PrerenderedInval; + class LayerContent : public SkRefCnt { public: virtual int width() = 0; virtual int height() = 0; - virtual bool isEmpty() = 0; + virtual bool isEmpty() { return !width() || !height(); } virtual void checkForOptimisations() = 0; virtual bool hasText() = 0; virtual void draw(SkCanvas* canvas) = 0; + virtual PrerenderedInval* prerenderForRect(const IntRect& dirty) { return 0; } + virtual void clearPrerenders() { }; virtual void serialize(SkWStream* stream) = 0; diff --git a/Source/WebCore/platform/graphics/android/layers/PicturePileLayerContent.cpp b/Source/WebCore/platform/graphics/android/layers/PicturePileLayerContent.cpp new file mode 100644 index 0000000..b648e72 --- /dev/null +++ b/Source/WebCore/platform/graphics/android/layers/PicturePileLayerContent.cpp @@ -0,0 +1,41 @@ +#include "config.h" +#include "PicturePileLayerContent.h" + +#include "SkCanvas.h" +#include "SkPicture.h" + +namespace WebCore { + +PicturePileLayerContent::PicturePileLayerContent(const PicturePile& picturePile) + : m_picturePile(picturePile) +{ +} + +void PicturePileLayerContent::draw(SkCanvas* canvas) +{ + android::Mutex::Autolock lock(m_drawLock); + m_picturePile.draw(canvas); +} + +void PicturePileLayerContent::serialize(SkWStream* stream) +{ + if (!stream) + return; + SkPicture picture; + draw(picture.beginRecording(width(), height(), + SkPicture::kUsePathBoundsForClip_RecordingFlag)); + picture.endRecording(); + picture.serialize(stream); +} + +PrerenderedInval* PicturePileLayerContent::prerenderForRect(const IntRect& dirty) +{ + return m_picturePile.prerenderedInvalForArea(dirty); +} + +void PicturePileLayerContent::clearPrerenders() +{ + m_picturePile.clearPrerenders(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/android/layers/PictureSetLayerContent.h b/Source/WebCore/platform/graphics/android/layers/PicturePileLayerContent.h index 61fc3f4..4216617 100644 --- a/Source/WebCore/platform/graphics/android/layers/PictureSetLayerContent.h +++ b/Source/WebCore/platform/graphics/android/layers/PicturePileLayerContent.h @@ -23,31 +23,31 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PictureSetLayerContent_h -#define PictureSetLayerContent_h +#ifndef PicturePileLayerContent_h +#define PicturePileLayerContent_h #include "LayerContent.h" -#include "PictureSet.h" +#include "PicturePile.h" namespace WebCore { -class PictureSetLayerContent : public LayerContent { +class PicturePileLayerContent : public LayerContent { public: - PictureSetLayerContent(const android::PictureSet& pictureSet); - ~PictureSetLayerContent(); + PicturePileLayerContent(const PicturePile& picturePile); - virtual int width() { return m_pictureSet.width(); } - virtual int height() { return m_pictureSet.height(); } - virtual bool isEmpty() { return m_pictureSet.isEmpty(); } + virtual int width() { return m_picturePile.size().width(); } + virtual int height() { return m_picturePile.size().height(); } virtual void checkForOptimisations() {} virtual bool hasText() { return true; } virtual void draw(SkCanvas* canvas); virtual void serialize(SkWStream* stream); + virtual PrerenderedInval* prerenderForRect(const IntRect& dirty); + virtual void clearPrerenders(); private: - android::PictureSet m_pictureSet; + PicturePile m_picturePile; }; } // WebCore -#endif // PictureLayerContent_h +#endif // PicturePileLayerContent_h diff --git a/Source/WebCore/platform/graphics/android/layers/PictureSetLayerContent.cpp b/Source/WebCore/platform/graphics/android/layers/PictureSetLayerContent.cpp deleted file mode 100644 index 8b72b0a..0000000 --- a/Source/WebCore/platform/graphics/android/layers/PictureSetLayerContent.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "config.h" -#include "PictureSetLayerContent.h" - -#include "SkCanvas.h" -#include "SkPicture.h" - -namespace WebCore { - -PictureSetLayerContent::PictureSetLayerContent(const android::PictureSet& pictureSet) -{ - m_pictureSet.set(pictureSet); -} - -PictureSetLayerContent::~PictureSetLayerContent() -{ - m_pictureSet.clear(); -} - -void PictureSetLayerContent::draw(SkCanvas* canvas) -{ - if (m_pictureSet.isEmpty()) - return; - - android::Mutex::Autolock lock(m_drawLock); - SkRect r = SkRect::MakeWH(width(), height()); - canvas->clipRect(r); - m_pictureSet.draw(canvas); -} - -void PictureSetLayerContent::serialize(SkWStream* stream) -{ - if (!stream) - return; - SkPicture picture; - draw(picture.beginRecording(m_pictureSet.width(), m_pictureSet.height(), - SkPicture::kUsePathBoundsForClip_RecordingFlag)); - picture.endRecording(); - picture.serialize(stream); -} - -} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/android/layers/PrerenderedInval.h b/Source/WebCore/platform/graphics/android/layers/PrerenderedInval.h new file mode 100644 index 0000000..91f385d --- /dev/null +++ b/Source/WebCore/platform/graphics/android/layers/PrerenderedInval.h @@ -0,0 +1,58 @@ +/* + * Copyright 2012, 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 PrerenderedInval_h +#define PrerenderedInval_h + +#include "IntRect.h" +#include "SkBitmap.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/ThreadSafeRefCounted.h> + +namespace WebCore { + +class PrerenderedInval : public ThreadSafeRefCounted<PrerenderedInval> { + WTF_MAKE_NONCOPYABLE(PrerenderedInval); +public: + SkBitmap bitmap; + IntRect area; + IntRect screenArea; + + static PassRefPtr<PrerenderedInval> create(const IntRect& ir) + { + return adoptRef(new PrerenderedInval(ir)); + } + +private: + PrerenderedInval(const IntRect& ir) + : area(ir) + {} +}; + +} // namespace WebCore + +#endif // PrerenderedInval_h diff --git a/Source/WebCore/platform/graphics/android/rendering/BaseRenderer.cpp b/Source/WebCore/platform/graphics/android/rendering/BaseRenderer.cpp index 8bb1755..6f679da 100644 --- a/Source/WebCore/platform/graphics/android/rendering/BaseRenderer.cpp +++ b/Source/WebCore/platform/graphics/android/rendering/BaseRenderer.cpp @@ -109,7 +109,6 @@ void BaseRenderer::renderTiledContent(TileRenderInfo& renderInfo) before = currentTimeMS(); } - setupPartialInval(renderInfo, &canvas); canvas.translate(-renderInfo.x * tileSize.width(), -renderInfo.y * tileSize.height()); canvas.scale(renderInfo.scale, renderInfo.scale); renderInfo.tilePainter->paint(&canvas); @@ -127,25 +126,10 @@ void BaseRenderer::renderTiledContent(TileRenderInfo& renderInfo) // only color the invalidated area SkPaint paint; paint.setARGB(color, 0, 255, 0); - if (renderInfo.invalRect) - canvas.drawIRect(*renderInfo.invalRect, paint); - else { - SkIRect rect; - rect.set(0, 0, tileSize.width(), tileSize.height()); - canvas.drawIRect(rect, paint); - } - - if (renderInfo.invalRect) { - // if partial inval... - int x = renderInfo.invalRect->fLeft; - int y = renderInfo.invalRect->fTop; - int w = renderInfo.invalRect->width(); - int h = renderInfo.invalRect->height(); - - paint.setARGB(128, 255, 255, 0); - canvas.drawLine(x, y, x + w, y + h, paint); - canvas.drawLine(x, y + h, x + w, y, paint); - } + SkIRect rect; + rect.set(0, 0, tileSize.width(), tileSize.height()); + canvas.drawIRect(rect, paint); + drawTileInfo(&canvas, renderInfo, updateCount, after - before); // paint the tile boundaries diff --git a/Source/WebCore/platform/graphics/android/rendering/BaseRenderer.h b/Source/WebCore/platform/graphics/android/rendering/BaseRenderer.h index f225871..b25a50e 100644 --- a/Source/WebCore/platform/graphics/android/rendering/BaseRenderer.h +++ b/Source/WebCore/platform/graphics/android/rendering/BaseRenderer.h @@ -49,9 +49,6 @@ struct TileRenderInfo { // current scale factor float scale; - // inval rectangle with coordinates in the tile's coordinate space - SkIRect* invalRect; - // the expected size of the tile SkSize tileSize; @@ -89,7 +86,6 @@ public: protected: virtual void setupCanvas(const TileRenderInfo& renderInfo, SkCanvas* canvas) = 0; - virtual void setupPartialInval(const TileRenderInfo& renderInfo, SkCanvas* canvas) {} virtual void renderingComplete(const TileRenderInfo& renderInfo, SkCanvas* canvas) = 0; virtual void checkForPureColor(TileRenderInfo& renderInfo, SkCanvas* canvas) = 0; diff --git a/Source/WebCore/platform/graphics/android/rendering/GLUtils.cpp b/Source/WebCore/platform/graphics/android/rendering/GLUtils.cpp index 7206c98..32f353c 100644 --- a/Source/WebCore/platform/graphics/android/rendering/GLUtils.cpp +++ b/Source/WebCore/platform/graphics/android/rendering/GLUtils.cpp @@ -418,10 +418,6 @@ bool GLUtils::skipTransferForPureColor(const TileRenderInfo* renderInfo, bool skipTransfer = false; Tile* tilePtr = renderInfo->baseTile; - // TODO: use pure color for partial invals as well - if (renderInfo->invalRect) - return false; - if (tilePtr) { TileTexture* tileTexture = tilePtr->backTexture(); // Check the bitmap, and make everything ready here. @@ -611,6 +607,33 @@ void GLUtils::clearBackgroundIfOpaque(const Color* backgroundColor) } } +bool GLUtils::deepCopyBitmapSubset(const SkBitmap& sourceBitmap, + SkBitmap& subset, int leftOffset, int topOffset) +{ + sourceBitmap.lockPixels(); + subset.lockPixels(); + char* srcPixels = (char*) sourceBitmap.getPixels(); + char* dstPixels = (char*) subset.getPixels(); + if (!dstPixels || !srcPixels || !subset.lockPixelsAreWritable()) { + ALOGD("no pixels :( %p, %p (writable=%d)", srcPixels, dstPixels, + subset.lockPixelsAreWritable()); + subset.unlockPixels(); + sourceBitmap.unlockPixels(); + return false; + } + int srcRowSize = sourceBitmap.rowBytes(); + int destRowSize = subset.rowBytes(); + for (int i = 0; i < subset.height(); i++) { + int srcOffset = (i + topOffset) * srcRowSize; + srcOffset += (leftOffset * sourceBitmap.bytesPerPixel()); + int dstOffset = i * destRowSize; + memcpy(dstPixels + dstOffset, srcPixels + srcOffset, destRowSize); + } + subset.unlockPixels(); + sourceBitmap.unlockPixels(); + return true; +} + } // namespace WebCore #endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/android/rendering/GLUtils.h b/Source/WebCore/platform/graphics/android/rendering/GLUtils.h index a235458..1b69d6c 100644 --- a/Source/WebCore/platform/graphics/android/rendering/GLUtils.h +++ b/Source/WebCore/platform/graphics/android/rendering/GLUtils.h @@ -29,6 +29,7 @@ #if USE(ACCELERATED_COMPOSITING) #include "Color.h" +#include "IntRect.h" #include "SkBitmap.h" #include "SkMatrix.h" #include "TransformationMatrix.h" @@ -73,7 +74,8 @@ public: static GLuint createTileGLTexture(int width, int height); static void createTextureWithBitmap(GLuint texture, const SkBitmap& bitmap, GLint filter = GL_LINEAR); - static void updateTextureWithBitmap(GLuint texture, const SkBitmap& bitmap, const IntRect&, GLint filter = GL_LINEAR); + static void updateTextureWithBitmap(GLuint texture, const SkBitmap& bitmap, + const IntRect& inval = IntRect(), GLint filter = GL_LINEAR); static void createEGLImageFromTexture(GLuint texture, EGLImageKHR* image); static void createTextureFromEGLImage(GLuint texture, EGLImageKHR image, GLint filter = GL_LINEAR); @@ -82,6 +84,8 @@ public: static bool updateSharedSurfaceTextureWithBitmap(ANativeWindow* anw, const SkBitmap& bitmap); static void convertToTransformationMatrix(const float* matrix, TransformationMatrix& transformMatrix); + static bool deepCopyBitmapSubset(const SkBitmap& sourceBitmap, + SkBitmap& subset, int left, int top); static bool isPureColorBitmap(const SkBitmap& bitmap, Color& pureColor); static bool skipTransferForPureColor(const TileRenderInfo* renderInfo, const SkBitmap& bitmap); diff --git a/Source/WebCore/platform/graphics/android/rendering/GaneshRenderer.cpp b/Source/WebCore/platform/graphics/android/rendering/GaneshRenderer.cpp index 208adb6..d779af4 100644 --- a/Source/WebCore/platform/graphics/android/rendering/GaneshRenderer.cpp +++ b/Source/WebCore/platform/graphics/android/rendering/GaneshRenderer.cpp @@ -84,16 +84,6 @@ void GaneshRenderer::setupCanvas(const TileRenderInfo& renderInfo, SkCanvas* can canvas->setDevice(device); } -void GaneshRenderer::setupPartialInval(const TileRenderInfo& renderInfo, SkCanvas* canvas) -{ - // set the clip to our invalRect - SkRect clipRect = SkRect::MakeLTRB(renderInfo.invalRect->fLeft, - renderInfo.invalRect->fTop, - renderInfo.invalRect->fRight, - renderInfo.invalRect->fBottom); - canvas->clipRect(clipRect); -} - void GaneshRenderer::renderingComplete(const TileRenderInfo& renderInfo, SkCanvas* canvas) { ALOGV("rendered to tile (%d,%d)", renderInfo.x, renderInfo.y); diff --git a/Source/WebCore/platform/graphics/android/rendering/GaneshRenderer.h b/Source/WebCore/platform/graphics/android/rendering/GaneshRenderer.h index d7eda24..cdd9f3e 100644 --- a/Source/WebCore/platform/graphics/android/rendering/GaneshRenderer.h +++ b/Source/WebCore/platform/graphics/android/rendering/GaneshRenderer.h @@ -47,7 +47,6 @@ public: protected: virtual void setupCanvas(const TileRenderInfo& renderInfo, SkCanvas* canvas); - virtual void setupPartialInval(const TileRenderInfo& renderInfo, SkCanvas* canvas); virtual void renderingComplete(const TileRenderInfo& renderInfo, SkCanvas* canvas); virtual void checkForPureColor(TileRenderInfo& renderInfo, SkCanvas* canvas) { renderInfo.isPureColor = false; diff --git a/Source/WebCore/platform/graphics/android/rendering/RasterRenderer.cpp b/Source/WebCore/platform/graphics/android/rendering/RasterRenderer.cpp index b880eef..47e5c17 100644 --- a/Source/WebCore/platform/graphics/android/rendering/RasterRenderer.cpp +++ b/Source/WebCore/platform/graphics/android/rendering/RasterRenderer.cpp @@ -80,7 +80,7 @@ void RasterRenderer::setupCanvas(const TileRenderInfo& renderInfo, SkCanvas* can ALOGV("setupCanvas use background on Base Layer %x", background->rgb()); g_bitmap->setIsOpaque(!background->hasAlpha()); g_bitmap->eraseARGB(background->alpha(), background->red(), - background->green(), background->blue()); + background->green(), background->blue()); } SkDevice* device = new SkDevice(*g_bitmap); @@ -88,15 +88,6 @@ void RasterRenderer::setupCanvas(const TileRenderInfo& renderInfo, SkCanvas* can canvas->setDevice(device); device->unref(); - - // If we have a partially painted bitmap - if (renderInfo.invalRect) { - SkRect clipRect = SkRect::MakeWH(renderInfo.invalRect->width(), - renderInfo.invalRect->height()); - // ensure the canvas origin is translated to the coordinates of our inval rect - canvas->clipRect(clipRect); - canvas->translate(-renderInfo.invalRect->fLeft, -renderInfo.invalRect->fTop); - } } void RasterRenderer::renderingComplete(const TileRenderInfo& renderInfo, SkCanvas* canvas) @@ -107,7 +98,8 @@ void RasterRenderer::renderingComplete(const TileRenderInfo& renderInfo, SkCanva void RasterRenderer::checkForPureColor(TileRenderInfo& renderInfo, SkCanvas* canvas) { - renderInfo.isPureColor = GLUtils::isPureColorBitmap(*g_bitmap, renderInfo.pureColor); + const SkBitmap& bitmap = canvas->getDevice()->accessBitmap(false); + renderInfo.isPureColor = GLUtils::isPureColorBitmap(bitmap, renderInfo.pureColor); } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/android/rendering/Surface.cpp b/Source/WebCore/platform/graphics/android/rendering/Surface.cpp index 13c7d27..652c165 100644 --- a/Source/WebCore/platform/graphics/android/rendering/Surface.cpp +++ b/Source/WebCore/platform/graphics/android/rendering/Surface.cpp @@ -33,9 +33,13 @@ #include "BaseLayerAndroid.h" #include "ClassTracker.h" #include "LayerAndroid.h" +#include "LayerContent.h" #include "GLWebViewState.h" +#include "PrerenderedInval.h" #include "SkCanvas.h" #include "SurfaceBacking.h" +#include "Tile.h" +#include "TileTexture.h" #include "TilesManager.h" #include <wtf/text/CString.h> @@ -181,7 +185,7 @@ bool Surface::useAggressiveRendering() || !m_background.hasAlpha()); } -void Surface::prepareGL(bool layerTilesDisabled) +void Surface::prepareGL(bool layerTilesDisabled, bool updateWithBlit) { bool tilesDisabled = layerTilesDisabled && !isBase(); if (!m_surfaceBacking) { @@ -210,8 +214,15 @@ void Surface::prepareGL(bool layerTilesDisabled) fullArea.x(), fullArea.y(), fullArea.width(), fullArea.height()); m_surfaceBacking->prepareGL(getFirstLayer()->state(), allowZoom, - prepareArea, fullArea, - this, useAggressiveRendering()); + prepareArea, fullArea, + this, useAggressiveRendering(), updateWithBlit); + if (updateWithBlit) { + for (size_t i = 0; i < m_layers.size(); i++) { + LayerContent* content = m_layers[i]->content(); + if (content) + content->clearPrerenders(); + } + } } } @@ -273,7 +284,28 @@ bool Surface::isMissingContent() return m_surfaceBacking->isMissingContent(); } -IntRect Surface::computePrepareArea() { +bool Surface::canUpdateWithBlit() +{ + // If we don't have a texture, we have nothing to update and thus can take + // the fast path + if (!needsTexture()) + return true; + // If we have a surface backing that isn't ready, we can't update with a blit + // If it is ready, then check to see if it is dirty. We can only call isDirty() + // if isReady() returns true + if (!m_surfaceBacking) + return false; + if (!m_surfaceBacking->isReady()) + return false; + if (!m_surfaceBacking->isDirty()) + return true; + if (!singleLayer()) + return false; + return getFirstLayer()->canUpdateWithBlit(); +} + +IntRect Surface::computePrepareArea() +{ IntRect area; if (!getFirstLayer()->contentIsScrollable() @@ -285,9 +317,8 @@ IntRect Surface::computePrepareArea() { double total = ((double) area.width()) * ((double) area.height()); if (total > MAX_UNCLIPPED_AREA) area = visibleArea(); - } else { + } else area = visibleArea(); - } return area; } @@ -355,6 +386,61 @@ Color* Surface::background() return &m_background; } +bool Surface::blitFromContents(Tile* tile) +{ + if (!singleLayer() || !tile || !getFirstLayer() || !getFirstLayer()->content()) + return false; + + LayerContent* content = getFirstLayer()->content(); + // Extract the dirty rect from the region. Note that this is *NOT* constrained + // to this tile + IntRect dirtyRect = tile->dirtyArea().getBounds(); + PrerenderedInval* prerenderedInval = content->prerenderForRect(dirtyRect); + if (!prerenderedInval || prerenderedInval->bitmap.isNull()) + return false; + SkBitmap sourceBitmap = prerenderedInval->bitmap; + // Calculate the screen rect that is dirty, then intersect it with the + // tile's screen rect so that we end up with the pixels we need to blit + FloatRect screenDirty = dirtyRect; + screenDirty.scale(tile->scale()); + IntRect enclosingScreenDirty = enclosingIntRect(screenDirty); + IntRect tileRect = IntRect(tile->x() * TilesManager::tileWidth(), + tile->y() * TilesManager::tileHeight(), + TilesManager::tileWidth(), + TilesManager::tileHeight()); + enclosingScreenDirty.intersect(tileRect); + if (enclosingScreenDirty.isEmpty()) + return false; + // Make sure the screen area we want to blit is contained by the + // prerendered screen area + if (!prerenderedInval->screenArea.contains(enclosingScreenDirty)) { + ALOGD("prerendered->screenArea " INT_RECT_FORMAT " doesn't contain " + "enclosingScreenDirty " INT_RECT_FORMAT, + INT_RECT_ARGS(prerenderedInval->screenArea), + INT_RECT_ARGS(enclosingScreenDirty)); + return false; + } + IntPoint origin = prerenderedInval->screenArea.location(); + SkBitmap subset; + subset.setConfig(sourceBitmap.config(), enclosingScreenDirty.width(), + enclosingScreenDirty.height()); + subset.allocPixels(); + + int topOffset = enclosingScreenDirty.y() - prerenderedInval->screenArea.y(); + int leftOffset = enclosingScreenDirty.x() - prerenderedInval->screenArea.x(); + if (!GLUtils::deepCopyBitmapSubset(sourceBitmap, subset, leftOffset, topOffset)) + return false; + // Now upload + SkIRect textureInval = SkIRect::MakeXYWH(enclosingScreenDirty.x() - tileRect.x(), + enclosingScreenDirty.y() - tileRect.y(), + enclosingScreenDirty.width(), + enclosingScreenDirty.height()); + GLUtils::updateTextureWithBitmap(tile->frontTexture()->m_ownTextureId, + subset, textureInval); + tile->onBlitUpdate(); + return true; +} + const TransformationMatrix* Surface::drawTransform() { // single layer surfaces query the layer's draw transform, while multi-layer diff --git a/Source/WebCore/platform/graphics/android/rendering/Surface.h b/Source/WebCore/platform/graphics/android/rendering/Surface.h index 0fced47..dc46fcf 100644 --- a/Source/WebCore/platform/graphics/android/rendering/Surface.h +++ b/Source/WebCore/platform/graphics/android/rendering/Surface.h @@ -49,11 +49,12 @@ public: bool tryUpdateSurface(Surface* oldSurface); void addLayer(LayerAndroid* layer, const TransformationMatrix& transform); - void prepareGL(bool layerTilesDisabled); + void prepareGL(bool layerTilesDisabled, bool updateWithBlit); bool drawGL(bool layerTilesDisabled); void swapTiles(); bool isReady(); bool isMissingContent(); + bool canUpdateWithBlit(); void computeTexturesAmount(TexturesResult* result); @@ -66,6 +67,7 @@ public: virtual bool paint(SkCanvas* canvas); virtual float opacity(); virtual Color* background(); + virtual bool blitFromContents(Tile* tile); private: IntRect computePrepareArea(); diff --git a/Source/WebCore/platform/graphics/android/rendering/SurfaceBacking.cpp b/Source/WebCore/platform/graphics/android/rendering/SurfaceBacking.cpp index f43472e..957aa63 100644 --- a/Source/WebCore/platform/graphics/android/rendering/SurfaceBacking.cpp +++ b/Source/WebCore/platform/graphics/android/rendering/SurfaceBacking.cpp @@ -57,7 +57,8 @@ SurfaceBacking::~SurfaceBacking() void SurfaceBacking::prepareGL(GLWebViewState* state, bool allowZoom, const IntRect& prepareArea, const IntRect& unclippedArea, - TilePainter* painter, bool aggressiveRendering) + TilePainter* painter, bool aggressiveRendering, + bool updateWithBlit) { float scale = state->scale(); if (scale > 1 && !allowZoom) @@ -113,7 +114,8 @@ void SurfaceBacking::prepareGL(GLWebViewState* state, bool allowZoom, // if the front grid hasn't already prepared, or needs to prepare // expanded bounds do so now m_frontTileGrid->prepareGL(state, m_scale, - prepareArea, unclippedArea, painter, prepareRegionFlags, false); + prepareArea, unclippedArea, painter, + prepareRegionFlags, false, updateWithBlit); } if (aggressiveRendering) { // prepare low res content diff --git a/Source/WebCore/platform/graphics/android/rendering/SurfaceBacking.h b/Source/WebCore/platform/graphics/android/rendering/SurfaceBacking.h index 61e3336..08cfc7c 100644 --- a/Source/WebCore/platform/graphics/android/rendering/SurfaceBacking.h +++ b/Source/WebCore/platform/graphics/android/rendering/SurfaceBacking.h @@ -42,7 +42,8 @@ public: ~SurfaceBacking(); void prepareGL(GLWebViewState* state, bool allowZoom, const IntRect& prepareArea, const IntRect& unclippedArea, - TilePainter* painter, bool aggressiveRendering); + TilePainter* painter, bool aggressiveRendering, + bool updateWithBlit); void swapTiles(); void drawGL(const IntRect& visibleArea, float opacity, const TransformationMatrix* transform, bool aggressiveRendering, @@ -59,6 +60,11 @@ public: return !m_zooming && m_frontTileGrid->isReady() && m_scale > 0; } + bool isDirty() + { + return m_frontTileGrid->isDirty(); + } + bool isMissingContent() { return m_zooming || m_frontTileGrid->isMissingContent(); diff --git a/Source/WebCore/platform/graphics/android/rendering/SurfaceCollection.cpp b/Source/WebCore/platform/graphics/android/rendering/SurfaceCollection.cpp index 24e196b..cf59044 100644 --- a/Source/WebCore/platform/graphics/android/rendering/SurfaceCollection.cpp +++ b/Source/WebCore/platform/graphics/android/rendering/SurfaceCollection.cpp @@ -88,8 +88,16 @@ void SurfaceCollection::prepareGL(const SkRect& visibleRect) updateLayerPositions(visibleRect); bool layerTilesDisabled = m_compositedRoot->state()->layersRenderingMode() > GLWebViewState::kClippedTextures; + bool updateWithBlit = true; + if (!layerTilesDisabled) { + for (unsigned int i = 0; i < m_surfaces.size(); i++) { + updateWithBlit &= m_surfaces[i]->canUpdateWithBlit(); + if (!updateWithBlit) + break; + } + } for (unsigned int i = 0; i < m_surfaces.size(); i++) - m_surfaces[i]->prepareGL(layerTilesDisabled); + m_surfaces[i]->prepareGL(layerTilesDisabled, updateWithBlit); } static inline bool compareSurfaceZ(const Surface* a, const Surface* b) diff --git a/Source/WebCore/platform/graphics/android/rendering/Tile.cpp b/Source/WebCore/platform/graphics/android/rendering/Tile.cpp index 2ee45b0..3af05f4 100644 --- a/Source/WebCore/platform/graphics/android/rendering/Tile.cpp +++ b/Source/WebCore/platform/graphics/android/rendering/Tile.cpp @@ -327,75 +327,7 @@ void Tile::paintBitmap(TilePainter* painter) const float tileWidth = renderInfo.tileSize.width(); const float tileHeight = renderInfo.tileSize.height(); - SkRegion::Iterator cliperator(dirtyArea); - - bool fullRepaint = false; - - if (m_fullRepaint - || textureInfo->m_width != tileWidth - || textureInfo->m_height != tileHeight) { - fullRepaint = true; - } - - // For now, only do full repaint - fullRepaint = true; - - if (!fullRepaint) { - // compute the partial inval area - SkIRect totalRect; - totalRect.set(0, 0, 0, 0); - float tileSurface = tileWidth * tileHeight; - float tileSurfaceCap = MAX_INVAL_AREA * tileSurface; - - // We join all the invals in the same tile for now - while (!fullRepaint && !cliperator.done()) { - SkRect realTileRect; - SkRect dirtyRect; - dirtyRect.set(cliperator.rect()); - bool intersect = intersectWithRect(x, y, tileWidth, tileHeight, - scale, dirtyRect, realTileRect); - if (intersect) { - // initialize finalRealRect to the rounded values of realTileRect - SkIRect finalRealRect; - realTileRect.roundOut(&finalRealRect); - - // stash the int values of the current width and height - const int iWidth = finalRealRect.width(); - const int iHeight = finalRealRect.height(); - - if (iWidth == tileWidth || iHeight == tileHeight) { - fullRepaint = true; - break; - } - - // translate the rect into tile space coordinates - finalRealRect.fLeft = finalRealRect.fLeft % static_cast<int>(tileWidth); - finalRealRect.fTop = finalRealRect.fTop % static_cast<int>(tileHeight); - finalRealRect.fRight = finalRealRect.fLeft + iWidth; - finalRealRect.fBottom = finalRealRect.fTop + iHeight; - totalRect.join(finalRealRect); - float repaintSurface = totalRect.width() * totalRect.height(); - - if (repaintSurface > tileSurfaceCap) { - fullRepaint = true; - break; - } - } - - cliperator.next(); - } - - if (!fullRepaint) { - renderInfo.invalRect = &totalRect; - m_renderer->renderTiledContent(renderInfo); - } - } - - // Do a full repaint if needed - if (fullRepaint) { - renderInfo.invalRect = 0; - m_renderer->renderTiledContent(renderInfo); - } + m_renderer->renderTiledContent(renderInfo); m_atomicSync.lock(); @@ -410,13 +342,7 @@ void Tile::paintBitmap(TilePainter* painter) if (m_scale != scale) m_dirty = true; - if (fullRepaint) - m_dirtyArea.setEmpty(); - else - m_dirtyArea.op(dirtyArea, SkRegion::kDifference_Op); - - if (!m_dirtyArea.isEmpty()) - m_dirty = true; + m_dirtyArea.setEmpty(); ALOGV("painted tile %p (%d, %d), texture %p, dirty=%d", this, x, y, texture, m_dirty); @@ -498,6 +424,15 @@ void Tile::backTextureTransferFail() { // whether validatePaint is called before or after, it won't do anything } +void Tile::onBlitUpdate() +{ + // The front texture was directly updated with a blit, so mark this as clean + android::AutoMutex lock(m_atomicSync); + m_dirty = false; + m_dirtyArea.setEmpty(); + m_state = Tile::UpToDate; +} + void Tile::validatePaint() { // ONLY CALL while m_atomicSync is locked (at the end of paintBitmap()) diff --git a/Source/WebCore/platform/graphics/android/rendering/Tile.h b/Source/WebCore/platform/graphics/android/rendering/Tile.h index c115f1c..9697b61 100644 --- a/Source/WebCore/platform/graphics/android/rendering/Tile.h +++ b/Source/WebCore/platform/graphics/android/rendering/Tile.h @@ -116,6 +116,7 @@ public: void markAsDirty(const SkRegion& dirtyArea); bool isDirty(); + const SkRegion& dirtyArea() { return m_dirtyArea; } virtual bool isRepaintPending(); void setRepaintPending(bool pending); float scale() const { return m_scale; } @@ -133,6 +134,7 @@ public: bool swapTexturesIfNeeded(); void backTextureTransfer(); void backTextureTransferFail(); + void onBlitUpdate(); // TextureOwner implementation virtual bool removeTexture(TileTexture* texture); diff --git a/Source/WebCore/platform/graphics/android/rendering/TileGrid.cpp b/Source/WebCore/platform/graphics/android/rendering/TileGrid.cpp index 8bf033e..bee42ae 100644 --- a/Source/WebCore/platform/graphics/android/rendering/TileGrid.cpp +++ b/Source/WebCore/platform/graphics/android/rendering/TileGrid.cpp @@ -34,6 +34,7 @@ #include "GLWebViewState.h" #include "PaintTileOperation.h" #include "Tile.h" +#include "TileTexture.h" #include "TilesManager.h" #include <wtf/CurrentTime.h> @@ -113,7 +114,7 @@ IntRect TileGrid::computeTilesArea(const IntRect& contentArea, float scale) ceilf(contentArea.width() * scale), ceilf(contentArea.height() * scale)); - ALOGV("TG %p prepare, scale %f, area %d x %d", this, scale, area.width(), area.height()); + ALOGV("TG prepare, scale %f, area %d x %d", scale, area.width(), area.height()); if (area.width() == 0 && area.height() == 0) { computedArea.setWidth(0); @@ -135,7 +136,8 @@ IntRect TileGrid::computeTilesArea(const IntRect& contentArea, float scale) void TileGrid::prepareGL(GLWebViewState* state, float scale, const IntRect& prepareArea, const IntRect& unclippedArea, - TilePainter* painter, int regionFlags, bool isLowResPrefetch) + TilePainter* painter, int regionFlags, bool isLowResPrefetch, + bool updateWithBlit) { // first, how many tiles do we need m_area = computeTilesArea(prepareArea, scale); @@ -181,11 +183,11 @@ void TileGrid::prepareGL(GLWebViewState* state, float scale, if (goingDown) { for (int j = 0; j < m_area.height(); j++) prepareTile(m_area.x() + i, m_area.y() + j, - painter, state, isLowResPrefetch, false); + painter, state, isLowResPrefetch, false, updateWithBlit); } else { for (int j = m_area.height() - 1; j >= 0; j--) prepareTile(m_area.x() + i, m_area.y() + j, - painter, state, isLowResPrefetch, false); + painter, state, isLowResPrefetch, false, updateWithBlit); } } } @@ -207,7 +209,7 @@ void TileGrid::prepareGL(GLWebViewState* state, float scale, for (int i = expandedArea.x(); i < expandedArea.maxX(); i++) for (int j = expandedArea.y(); j < expandedArea.maxY(); j++) if (!m_area.contains(i, j)) - prepareTile(i, j, painter, state, isLowResPrefetch, true); + prepareTile(i, j, painter, state, isLowResPrefetch, true, updateWithBlit); } } @@ -219,7 +221,8 @@ void TileGrid::markAsDirty(const SkRegion& invalRegion) } void TileGrid::prepareTile(int x, int y, TilePainter* painter, - GLWebViewState* state, bool isLowResPrefetch, bool isExpandPrefetch) + GLWebViewState* state, bool isLowResPrefetch, + bool isExpandPrefetch, bool shouldTryUpdateWithBlit) { Tile* tile = getTile(x, y); if (!tile) { @@ -232,6 +235,9 @@ void TileGrid::prepareTile(int x, int y, TilePainter* painter, tile->setContents(x, y, m_scale, isExpandPrefetch); + if (shouldTryUpdateWithBlit && tryBlitFromContents(tile, painter)) + return; + if (tile->isDirty() || !tile->frontTexture()) tile->reserveTexture(); @@ -243,6 +249,15 @@ void TileGrid::prepareTile(int x, int y, TilePainter* painter, } } +bool TileGrid::tryBlitFromContents(Tile* tile, TilePainter* painter) +{ + return tile->frontTexture() + && !tile->frontTexture()->isPureColor() + && tile->frontTexture()->m_ownTextureId + && !tile->isRepaintPending() + && painter->blitFromContents(tile); +} + Tile* TileGrid::getTile(int x, int y) { for (unsigned int i = 0; i <m_tiles.size(); i++) { diff --git a/Source/WebCore/platform/graphics/android/rendering/TileGrid.h b/Source/WebCore/platform/graphics/android/rendering/TileGrid.h index 2483e0e..e412979 100644 --- a/Source/WebCore/platform/graphics/android/rendering/TileGrid.h +++ b/Source/WebCore/platform/graphics/android/rendering/TileGrid.h @@ -51,13 +51,11 @@ public: void prepareGL(GLWebViewState* state, float scale, const IntRect& prepareArea, const IntRect& unclippedArea, TilePainter* painter, int regionFlags = StandardRegion, - bool isLowResPrefetch = false); + bool isLowResPrefetch = false, bool updateWithBlit = false); void swapTiles(); void drawGL(const IntRect& visibleArea, float opacity, const TransformationMatrix* transform, const Color* background = 0); - void prepareTile(int x, int y, TilePainter* painter, - GLWebViewState* state, bool isLowResPrefetch, bool isExpandPrefetch); void markAsDirty(const SkRegion& dirtyArea); Tile* getTile(int x, int y); @@ -67,11 +65,17 @@ public: bool isReady(); bool isMissingContent(); + bool isDirty() { return !m_dirtyRegion.isEmpty(); } int nbTextures(IntRect& area, float scale); private: + void prepareTile(int x, int y, TilePainter* painter, + GLWebViewState* state, bool isLowResPrefetch, + bool isExpandPrefetch, bool shouldTryUpdateWithBlit); void drawMissingRegion(const SkRegion& region, float opacity, const Color* tileBackground); + bool tryBlitFromContents(Tile* tile, TilePainter* painter); + WTF::Vector<Tile*> m_tiles; IntRect m_area; diff --git a/Source/WebCore/platform/graphics/android/rendering/TilePainter.h b/Source/WebCore/platform/graphics/android/rendering/TilePainter.h index 53dfadc..901db66 100644 --- a/Source/WebCore/platform/graphics/android/rendering/TilePainter.h +++ b/Source/WebCore/platform/graphics/android/rendering/TilePainter.h @@ -34,6 +34,7 @@ class SkCanvas; namespace WebCore { class Color; +class Tile; class TilePainter : public SkRefCnt { // TODO: investigate webkit threadsafe ref counting @@ -44,6 +45,7 @@ public: enum SurfaceType { Painted, Image }; virtual SurfaceType type() { return Painted; } virtual Color* background() { return 0; } + virtual bool blitFromContents(Tile* tile) { return false; } unsigned int getUpdateCount() { return m_updateCount; } void setUpdateCount(unsigned int updateCount) { m_updateCount = updateCount; } diff --git a/Source/WebCore/platform/graphics/android/rendering/TilesManager.cpp b/Source/WebCore/platform/graphics/android/rendering/TilesManager.cpp index 5693d28..6e22d25 100644 --- a/Source/WebCore/platform/graphics/android/rendering/TilesManager.cpp +++ b/Source/WebCore/platform/graphics/android/rendering/TilesManager.cpp @@ -471,12 +471,12 @@ bool TilesManager::updateContextIfChanged() return changed; } -float TilesManager::tileWidth() +int TilesManager::tileWidth() { return TILE_WIDTH; } -float TilesManager::tileHeight() +int TilesManager::tileHeight() { return TILE_HEIGHT; } diff --git a/Source/WebCore/platform/graphics/android/rendering/TilesManager.h b/Source/WebCore/platform/graphics/android/rendering/TilesManager.h index 834f36d..295acf6 100644 --- a/Source/WebCore/platform/graphics/android/rendering/TilesManager.h +++ b/Source/WebCore/platform/graphics/android/rendering/TilesManager.h @@ -87,8 +87,8 @@ public: int currentLayerTextureCount(); void setCurrentTextureCount(int newTextureCount); void setCurrentLayerTextureCount(int newTextureCount); - static float tileWidth(); - static float tileHeight(); + static int tileWidth(); + static int tileHeight(); void allocateTextures(); diff --git a/Source/WebCore/platform/graphics/android/rendering/TransferQueue.cpp b/Source/WebCore/platform/graphics/android/rendering/TransferQueue.cpp index df5ba6e..2316014 100644 --- a/Source/WebCore/platform/graphics/android/rendering/TransferQueue.cpp +++ b/Source/WebCore/platform/graphics/android/rendering/TransferQueue.cpp @@ -166,34 +166,14 @@ void TransferQueue::blitTileFromQueue(GLuint fboID, TileTexture* destTex, int textureWidth = destTex->getSize().width(); int textureHeight = destTex->getSize().height(); - IntRect inval = m_transferQueue[index].invalRect; - bool partialInval = !inval.isEmpty(); - - if (partialInval && frontTex) { - // recopy the previous texture to the new one, as - // the partial update will not cover the entire texture - glFramebufferTexture2D(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - frontTex->m_ownTextureId, - 0); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, - textureWidth, textureHeight); - } - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, srcTexId, 0); - if (!partialInval) { - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, - textureWidth, textureHeight); - } else { - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, inval.x(), inval.y(), 0, 0, - inval.width(), inval.height()); - } + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, + textureWidth, textureHeight); #else // Then set up the FBO and copy the SurfTex content in. @@ -412,8 +392,7 @@ void TransferQueue::updateDirtyTiles() if (m_transferQueue[index].uploadType == CpuUpload) { // Here we just need to upload the bitmap content to the GL Texture GLUtils::updateTextureWithBitmap(destTexture->m_ownTextureId, - *m_transferQueue[index].bitmap, - m_transferQueue[index].invalRect); + *m_transferQueue[index].bitmap); } else { if (!usedFboForUpload) { saveGLState(); @@ -517,13 +496,6 @@ void TransferQueue::addItemCommon(const TileRenderInfo* renderInfo, data->uploadType = type; IntRect inval(0, 0, 0, 0); - if (renderInfo->invalRect) { - inval.setX(renderInfo->invalRect->fLeft); - inval.setY(renderInfo->invalRect->fTop); - inval.setWidth(renderInfo->invalRect->width()); - inval.setHeight(renderInfo->invalRect->height()); - } - data->invalRect = inval; } // Note that there should be lock/unlock around this function call. diff --git a/Source/WebCore/platform/graphics/android/rendering/TransferQueue.h b/Source/WebCore/platform/graphics/android/rendering/TransferQueue.h index fe58336..b8563e3 100644 --- a/Source/WebCore/platform/graphics/android/rendering/TransferQueue.h +++ b/Source/WebCore/platform/graphics/android/rendering/TransferQueue.h @@ -91,7 +91,6 @@ public: TransferItemStatus status; Tile* savedTilePtr; TileTexture* savedTileTexturePtr; - IntRect invalRect; TextureUploadType uploadType; // This is only useful in Cpu upload code path, so it will be dynamically // lazily allocated. diff --git a/Source/WebKit/Android.mk b/Source/WebKit/Android.mk index 67da200..c3e9798 100644 --- a/Source/WebKit/Android.mk +++ b/Source/WebKit/Android.mk @@ -65,7 +65,7 @@ LOCAL_SRC_FILES += \ android/jni/JavaSharedClient.cpp \ android/jni/MIMETypeRegistry.cpp \ android/jni/MockGeolocation.cpp \ - android/jni/PictureSet.cpp \ + android/jni/PicturePile.cpp \ android/jni/WebCoreFrameBridge.cpp \ android/jni/WebCoreJni.cpp \ android/jni/WebFrameView.cpp \ diff --git a/Source/WebKit/android/AndroidLog.h b/Source/WebKit/android/AndroidLog.h index e483169..f034d35 100644 --- a/Source/WebKit/android/AndroidLog.h +++ b/Source/WebKit/android/AndroidLog.h @@ -53,7 +53,12 @@ extern FILE* gRenderTreeFile; #define DISPLAY_TREE_LOG_FILE "/sdcard/displayTree.txt" #define LAYERS_TREE_LOG_FILE "/sdcard/layersTree.plist" -#define TRACE_METHOD() ScopedTrace __st(ATRACE_TAG_WEBVIEW, __func__); +#define FLOAT_RECT_FORMAT "[x=%.2f,y=%.2f,w=%.2f,h=%.2f]" +#define FLOAT_RECT_ARGS(fr) fr.x(), fr.y(), fr.width(), fr.height() +#define INT_RECT_FORMAT "[x=%d,y=%d,w=%d,h=%d]" +#define INT_RECT_ARGS(ir) ir.x(), ir.y(), ir.width(), ir.height() + +#define TRACE_METHOD() android::ScopedTrace __st(ATRACE_TAG_WEBVIEW, __func__); #define TIME_METHOD() MethodTimer __method_timer(__func__) class MethodTimer { diff --git a/Source/WebKit/android/jni/PicturePile.cpp b/Source/WebKit/android/jni/PicturePile.cpp new file mode 100644 index 0000000..9ca3588 --- /dev/null +++ b/Source/WebKit/android/jni/PicturePile.cpp @@ -0,0 +1,309 @@ +/* + * Copyright 2012, 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. + */ + +#define LOG_TAG "PicturePile" +#define LOG_NDEBUG 1 + +#include "config.h" +#include "PicturePile.h" + +#include "AndroidLog.h" +#include "FloatRect.h" +#include "GraphicsContext.h" +#include "PlatformGraphicsContextSkia.h" +#include "SkCanvas.h" +#include "SkNWayCanvas.h" +#include "SkPicture.h" +#include "SkPixelRef.h" +#include "SkRect.h" +#include "SkRegion.h" + +#define ENABLE_PRERENDERED_INVALS true +#define MAX_OVERLAP_COUNT 2 +#define MAX_OVERLAP_AREA .7 + +namespace WebCore { + +static SkIRect toSkIRect(const IntRect& rect) { + return SkIRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height()); +} + +static IntRect extractClipBounds(SkCanvas* canvas, const IntSize& size) { + SkRect clip; + canvas->getClipBounds(&clip); + clip.intersect(0, 0, size.width(), size.height()); + return enclosingIntRect(clip); +} + +PicturePile::PicturePile(const PicturePile& other) + : m_size(other.m_size) + , m_pile(other.m_pile) + , m_webkitInvals(other.m_webkitInvals) +{ +} + +PicturePile::PicturePile(SkPicture* picture) +{ + m_size = IntSize(picture->width(), picture->height()); + PictureContainer pc(IntRect(0, 0, m_size.width(), m_size.height())); + pc.picture = picture; + pc.dirty = false; + m_pile.append(pc); +} + +void PicturePile::draw(SkCanvas* canvas) +{ + /* Loop down recursively, subtracting the previous clip from the SkRegion, + * stopping when the SkRegion is empty. This will still draw back-to-front, + * but it will clip out anything obscured. For performance reasons we use + * the rect bounds of the SkRegion for the clip, so this still can't be + * used for translucent surfaces + */ + TRACE_METHOD(); + IntRect clipBounds = extractClipBounds(canvas, m_size); + SkRegion clipRegion(toSkIRect(clipBounds)); + drawWithClipRecursive(canvas, clipRegion, m_pile.size() - 1); +} + +void PicturePile::clearPrerenders() +{ + for (size_t i = 0; i < m_pile.size(); i++) + m_pile[i].prerendered.clear(); +} + +void PicturePile::drawWithClipRecursive(SkCanvas* canvas, SkRegion& clipRegion, + int index) +{ + // TODO: Add some debug visualizations of this + if (index < 0 || clipRegion.isEmpty()) + return; + PictureContainer& pc = m_pile[index]; + IntRect intersection = clipRegion.getBounds(); + intersection.intersect(pc.area); + if (pc.picture && !intersection.isEmpty()) { + clipRegion.op(intersection, SkRegion::kDifference_Op); + drawWithClipRecursive(canvas, clipRegion, index - 1); + int saved = canvas->save(); + canvas->clipRect(intersection); + canvas->translate(pc.area.x(), pc.area.y()); + canvas->drawPicture(*pc.picture); + canvas->restoreToCount(saved); + } else + drawWithClipRecursive(canvas, clipRegion, index - 1); +} + +// Used by WebViewCore +void PicturePile::invalidate(const IntRect& dirtyRect) +{ + // This will typically happen if the document has been resized but we haven't + // drawn yet. As the first draw after a size change will do a full inval anyway, + // don't bother tracking individual rects + // TODO: Instead of clipping here, we should take the invals as given + // and when the size changes just inval the deltas. This prevents a full + // redraw for a page that grows + IntRect inval = dirtyRect; + inval.intersect(IntRect(0, 0, m_size.width(), m_size.height())); + if (inval.isEmpty()) { + ALOGV("Rejecting inval " INT_RECT_FORMAT, INT_RECT_ARGS(dirtyRect)); + return; + } + // TODO: Support multiple non-intersecting webkit invals + if (m_webkitInvals.size()) + m_webkitInvals[0].unite(inval); + else + m_webkitInvals.append(inval); +} + +void PicturePile::setSize(const IntSize& size) +{ + if (m_size == size) + return; + m_size = size; + // TODO: See above about just adding invals for new content + m_pile.clear(); + m_webkitInvals.clear(); + IntRect area(0, 0, size.width(), size.height()); + m_webkitInvals.append(area); + m_pile.append(area); +} + +void PicturePile::updatePicturesIfNeeded(PicturePainter* painter) +{ + applyWebkitInvals(); + for (size_t i = 0; i < m_pile.size(); i++) { + PictureContainer& pc = m_pile[i]; + if (pc.dirty) + updatePicture(painter, pc); + } +} + +void PicturePile::updatePicture(PicturePainter* painter, PictureContainer& pc) +{ + /* The ref counting here is a bit unusual. What happens is begin/end recording + * will ref/unref the recording canvas. However, 'canvas' might be pointing + * at an SkNWayCanvas instead of the recording canvas, which needs to be + * unref'd. Thus what we do is ref the recording canvas so that we can + * always unref whatever canvas we have at the end. + */ + TRACE_METHOD(); + SkPicture* picture = new SkPicture(); + SkCanvas* canvas = picture->beginRecording(pc.area.width(), pc.area.height(), + SkPicture::kUsePathBoundsForClip_RecordingFlag); + SkSafeRef(canvas); + canvas->translate(-pc.area.x(), -pc.area.y()); + IntRect drawArea = pc.area; + if (pc.prerendered.get()) { + SkCanvas* prerender = painter->createPrerenderCanvas(pc.prerendered.get()); + if (!prerender) { + ALOGV("Failed to create prerendered for " INT_RECT_FORMAT, + INT_RECT_ARGS(pc.prerendered->area)); + pc.prerendered.clear(); + } else { + drawArea.unite(pc.prerendered->area); + SkNWayCanvas* nwayCanvas = new SkNWayCanvas(drawArea.width(), drawArea.height()); + nwayCanvas->addCanvas(canvas); + nwayCanvas->addCanvas(prerender); + SkSafeUnref(canvas); + SkSafeUnref(prerender); + canvas = nwayCanvas; + } + } + WebCore::PlatformGraphicsContextSkia pgc(canvas); + WebCore::GraphicsContext gc(&pgc); + ALOGV("painting picture: " INT_RECT_FORMAT, INT_RECT_ARGS(drawArea)); + painter->paintContents(&gc, drawArea); + SkSafeUnref(canvas); + picture->endRecording(); + + SkSafeUnref(pc.picture); + pc.picture = picture; + pc.dirty = false; +} + +void PicturePile::reset() +{ + m_size = IntSize(0,0); + m_pile.clear(); + m_webkitInvals.clear(); +} + +void PicturePile::applyWebkitInvals() +{ + m_dirtyRegion.setEmpty(); + if (!m_webkitInvals.size()) + return; + // Build the invals (TODO: Support multiple inval regions) + IntRect inval = m_webkitInvals[0]; + m_dirtyRegion.setRect(toSkIRect(inval)); + for (size_t i = 1; i < m_webkitInvals.size(); i++) { + inval.unite(m_webkitInvals[i]); + m_dirtyRegion.op(toSkIRect(m_webkitInvals[i]), SkRegion::kUnion_Op); + } + m_webkitInvals.clear(); + ALOGV("Webkit inval: " INT_RECT_FORMAT, INT_RECT_ARGS(inval)); + if (inval.isEmpty()) + return; + + // Find the overlaps + Vector<int> overlaps; + for (size_t i = 0; i < m_pile.size(); i++) { + PictureContainer& pc = m_pile[i]; + if (pc.area.contains(inval)) { + if (pc.dirty) { + ALOGV("Found already dirty intersection"); + return; + } + if (pc.area == inval) { + appendToPile(inval); + return; + } + // Don't count the base surface as an overlap + if (pc.area.size() != m_size) + overlaps.append(i); + } else if (pc.area.intersects(inval)) + overlaps.append(i); + } + + if (overlaps.size() >= MAX_OVERLAP_COUNT) { + ALOGV("Exceeds overlap count"); + IntRect overlap = inval; + for (int i = (int) overlaps.size() - 1; i >= 0; i--) { + overlap.unite(m_pile[overlaps[i]].area); + m_pile.remove(overlaps[i]); + } + float overlapArea = overlap.width() * overlap.height(); + float totalArea = m_size.width() * m_size.height(); + if (overlapArea / totalArea > MAX_OVERLAP_AREA) + overlap = IntRect(0, 0, m_size.width(), m_size.height()); + appendToPile(overlap, inval); + return; + } + + // Append! + appendToPile(inval); +} + +void PicturePile::appendToPile(const IntRect& inval, const IntRect& originalInval) +{ + ALOGV("Adding inval " INT_RECT_FORMAT " for original inval " INT_RECT_FORMAT, + INT_RECT_ARGS(inval), INT_RECT_ARGS(originalInval)); + // Remove any entries this obscures + for (int i = (int) m_pile.size() - 1; i >= 0; i--) { + if (inval.contains(m_pile[i].area)) + m_pile.remove(i); + } + PictureContainer container(inval); + if (ENABLE_PRERENDERED_INVALS) { + container.prerendered = PrerenderedInval::create(originalInval.isEmpty() + ? inval : originalInval); + } + m_pile.append(container); +} + +PrerenderedInval* PicturePile::prerenderedInvalForArea(const IntRect& area) +{ + ALOGV("Checking for prerendered inval for area " INT_RECT_FORMAT, + INT_RECT_ARGS(area)); + for (int i = (int) m_pile.size() - 1; i >= 0; i--) { + if (m_pile[i].area.intersects(area)) { + RefPtr<PrerenderedInval> inval = m_pile[i].prerendered; + if (inval.get() && inval->area.contains(area)) { + ALOGV("Returning prerendered %p for area " INT_RECT_FORMAT, + m_pile[i].prerendered.get(), INT_RECT_ARGS(area)); + return inval.get(); + } + if (inval.get()) { + ALOGV("Prerendered area doesn't contain requested area; prerendered=" + INT_RECT_FORMAT, INT_RECT_ARGS(inval->area)); + } else + ALOGV("No prerendered in intersection"); + return 0; + } + } + ALOGV("No containers found"); + return 0; +} + +} // namespace WebCore diff --git a/Source/WebKit/android/jni/PicturePile.h b/Source/WebKit/android/jni/PicturePile.h new file mode 100644 index 0000000..b28a792 --- /dev/null +++ b/Source/WebKit/android/jni/PicturePile.h @@ -0,0 +1,121 @@ +/* + * Copyright 2012, 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 PicturePile_h +#define PicturePile_h + +#include "IntRect.h" +#include "IntSize.h" +#include "PrerenderedInval.h" +#include "SkBitmap.h" +#include "SkRegion.h" +#include "SkRefCnt.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/ThreadSafeRefCounted.h> +#include <wtf/Vector.h> + +class SkPicture; +class SkCanvas; + +namespace WebCore { + +class GraphicsContext; + +class PicturePainter { +public: + virtual void paintContents(GraphicsContext* gc, IntRect& dirty) = 0; + virtual SkCanvas* createPrerenderCanvas(PrerenderedInval* prerendered) + { + return 0; + } + virtual ~PicturePainter() {} +}; + +class PictureContainer { +public: + SkPicture* picture; + IntRect area; + bool dirty; + RefPtr<PrerenderedInval> prerendered; + + PictureContainer(const IntRect& area) + : picture(0) + , area(area) + , dirty(true) + {} + + PictureContainer(const PictureContainer& other) + : picture(other.picture) + , area(other.area) + , dirty(other.dirty) + , prerendered(other.prerendered) + { + SkSafeRef(picture); + } + + ~PictureContainer() + { + SkSafeUnref(picture); + } +}; + +class PicturePile { +public: + PicturePile() {} + PicturePile(const PicturePile& other); + PicturePile(SkPicture* picture); + + const IntSize& size() { return m_size; } + + void clearPrerenders(); + + // used by PicturePileLayerContents + void draw(SkCanvas* canvas); + + // Used by WebViewCore + void invalidate(const IntRect& dirtyRect); + void setSize(const IntSize& size); + void updatePicturesIfNeeded(PicturePainter* painter); + void reset(); + SkRegion& dirtyRegion() { return m_dirtyRegion; } + PrerenderedInval* prerenderedInvalForArea(const IntRect& area); + +private: + void applyWebkitInvals(); + void updatePicture(PicturePainter* painter, PictureContainer& container); + void appendToPile(const IntRect& inval, const IntRect& originalInval = IntRect()); + void drawWithClipRecursive(SkCanvas* canvas, SkRegion& clipRegion, int index); + + IntSize m_size; + Vector<PictureContainer> m_pile; + Vector<IntRect> m_webkitInvals; + SkRegion m_dirtyRegion; +}; + +} // namespace android + +#endif // PicturePile_h diff --git a/Source/WebKit/android/jni/PictureSet.cpp b/Source/WebKit/android/jni/PictureSet.cpp deleted file mode 100644 index 8beb0ef..0000000 --- a/Source/WebKit/android/jni/PictureSet.cpp +++ /dev/null @@ -1,1244 +0,0 @@ -/* - * Copyright 2008, 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. - */ - -#define LOG_TAG "PictureSet" -#define LOG_NDEBUG 1 - -#include "config.h" -#include "PictureSet.h" - -#include "AndroidLog.h" -#include "android_graphics.h" -#include "SkBounder.h" -#include "SkCanvas.h" -#include "SkPicture.h" -#include "SkRect.h" -#include "SkRegion.h" -#include "SkStream.h" - -#include "PlatformGraphicsContext.h" - -#define MAX_DRAW_TIME 100 -#define MIN_SPLITTABLE 400 -#define MAX_ADDITIONAL_AREA 0.65 -#define MAX_ADDITIONAL_PICTURES 32 - -#define BUCKET_SIZE 1024 -#define MAX_BUCKET_COUNT_X 16 -#define MAX_BUCKET_COUNT_Y 64 - -#if PICTURE_SET_DEBUG -class MeasureStream : public SkWStream { -public: - MeasureStream() : mTotal(0) {} - virtual bool write(const void* , size_t size) { - mTotal += size; - return true; - } - size_t mTotal; -}; -#endif - -namespace android { - -PictureSet::PictureSet() : -#ifdef FAST_PICTURESET - mBucketSizeX(BUCKET_SIZE), mBucketSizeY(BUCKET_SIZE), - mBucketCountX(0), mBucketCountY(0), -#endif - mHeight(0), mWidth(0) -{ - setDimensions(0, 0); - mBaseArea = mAdditionalArea = 0; -} - -PictureSet::PictureSet(SkPicture* picture) : -#ifdef FAST_PICTURESET - mBucketSizeX(BUCKET_SIZE), mBucketSizeY(BUCKET_SIZE), - mBucketCountX(0), mBucketCountY(0), -#endif - mHeight(0), mWidth(0) -{ - mBaseArea = mAdditionalArea = 0; - if (!picture) { - setDimensions(0, 0); - return; - } - setDimensions(picture->width(), picture->height()); - mBaseArea = mWidth * mHeight; -#ifdef FAST_PICTURESET - SkIRect area; - area.set(0, 0, mWidth, mHeight); - splitAdd(area); - WTF::Vector<Bucket*>* buckets = bucketsToUpdate(); - for (unsigned int i = 0; i < buckets->size(); i++) { - Bucket* bucket = (*buckets)[i]; - for (unsigned int j = 0; j < bucket->size(); j++) { - BucketPicture& bucketPicture = (*bucket)[j]; - const SkIRect& inval = bucketPicture.mRealArea; - SkPicture *splitPicture = new SkPicture(); - SkCanvas *canvas = splitPicture->beginRecording( - inval.width(), inval.height(), - SkPicture::kUsePathBoundsForClip_RecordingFlag); - canvas->translate(-inval.fLeft, -inval.fTop); - picture->draw(canvas); - splitPicture->endRecording(); - SkSafeUnref(bucketPicture.mPicture); - bucketPicture.mPicture = splitPicture; - } - } - buckets->clear(); -#else - Pictures pictureAndBounds; - pictureAndBounds.mPicture = picture; - SkSafeRef(pictureAndBounds.mPicture); - pictureAndBounds.mEmpty = false; - pictureAndBounds.mArea.set(0, 0, mWidth, mHeight); - pictureAndBounds.mSplit = false; - pictureAndBounds.mBase = true; - pictureAndBounds.mElapsed = 0; - pictureAndBounds.mWroteElapsed = false; - mPictures.append(pictureAndBounds); -#endif // FAST_PICTURESET -} - -PictureSet::~PictureSet() -{ - clear(); -} - -#ifdef FAST_PICTURESET -#else -void PictureSet::add(const Pictures* temp) -{ - Pictures pictureAndBounds = *temp; - SkSafeRef(pictureAndBounds.mPicture); -#ifdef CONTEXT_RECORDING - SkSafeRef(pictureAndBounds.mGraphicsOperationCollection); -#endif - pictureAndBounds.mWroteElapsed = false; - mPictures.append(pictureAndBounds); -} -#endif // FAST_PICTURESET - -void PictureSet::add(const SkRegion& area, uint32_t elapsed, bool split) -{ - if (area.isRect()) { -#ifdef FAST_PICTURESET - splitAdd(area.getBounds()); -#else - add(area.getBounds(), elapsed, split, false); -#endif // FAST_PICTURESET - } else { - SkRegion::Iterator cliperator(area); - while (!cliperator.done()) { - SkIRect ir = cliperator.rect(); -#ifdef FAST_PICTURESET - splitAdd(ir); -#else - add(ir, elapsed, split, false); -#endif // FAST_PICTURESET - cliperator.next(); - } - } -} - -#ifdef FAST_PICTURESET - -Bucket* PictureSet::getBucket(int x, int y) -{ - // only create buckets for valid, positive coordinates, ignore and return - // NULL otherwise - if (x < 0 || y < 0) - return 0; - - BucketPosition position(x+1, y+1); - if (!mBuckets.contains(position)) { - ALOGV("PictureSet::getBucket(%d, %d) adding new bucket", x, y); - Bucket* bucket = new Bucket(); - mBuckets.add(position, bucket); - } - return mBuckets.get(position); -} - -void PictureSet::displayBucket(Bucket* bucket) -{ - BucketPicture* first = bucket->begin(); - BucketPicture* last = bucket->end(); - for (BucketPicture* current = first; current != last; current++) { - ALOGD("- in %x, bucketPicture %d,%d,%d,%d - %dx%d, picture: %x, base: %x", - bucket, - current->mArea.fLeft, - current->mArea.fTop, - current->mArea.fRight, - current->mArea.fBottom, - current->mArea.width(), - current->mArea.height(), - current->mPicture, - current->mBase); - } -} - -void PictureSet::displayBuckets() -{ - ALOGD("\n\n****** DISPLAY BUCKETS ON PictureSet %x ******", this); - for (BucketMap::iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) { - ALOGD("\n*** Bucket %x for %d, %d", iter->second, iter->first.first, iter->first.second); - displayBucket(iter->second); - } - ALOGD("\n****** END OF DISPLAY BUCKETS ******\n\n"); -} - -// When we receive an inval in a Bucket, we try to see if we intersect with -// existing invals/pictures in the Bucket. -void PictureSet::addToBucket(Bucket* bucket, int dx, int dy, SkIRect& rect) -{ - bool resetBase = false; - - SkIRect totalArea = rect; - BucketPicture* first = bucket->begin(); - BucketPicture* last = bucket->end(); - - // If the inval covers a large area of the base inval, let's repaint the - // entire bucket. - if (rect.width() * rect.height() > MAX_ADDITIONAL_AREA * mBucketSizeX * mBucketSizeY) - resetBase = true; - - // let's gather all the BucketPicture intersecting with the new invalidated - // area, collect their area and remove their picture - for (BucketPicture* current = first; current != last; current++) { - bool remove = resetBase; - bool intersect = false; - - if (!remove) - intersect = SkIRect::Intersects(current->mArea, rect); - // If the current picture is not a base, and we intersect, remove it - if (!remove && !current->mBase && intersect) - remove = true; - // If the current picture is a base, check if the new inval completely - // contains the base, and if so remove it. - if (!remove && current->mBase && rect.contains(current->mArea)) - remove = true; - // If the current picture is a base and it intersects, - // also check that it fully covers the bucket -- otherwise, - // let's aggregate it with the new inval. - if (!remove && current->mBase && intersect - && (current->mArea.width() < mBucketSizeX || current->mArea.height() < mBucketSizeY)) { - remove = true; - } - - if (remove) { - totalArea.join(current->mArea); - current->mBase = false; - current->mArea.setEmpty(); - SkSafeUnref(current->mPicture); - current->mPicture = 0; - } - } - - // Now, let's add the new BucketPicture to the list, with the correct - // area that needs to be repainted - SkRegion region; - SkIRect area = totalArea; - area.offset(dx, dy); - BucketPicture picture = { 0, totalArea, area, false }; - - bucket->append(picture); - - first = bucket->begin(); - last = bucket->end(); - - bool clearUp = false; - if (last - first > MAX_ADDITIONAL_PICTURES) { - // too many pictures in the bucket, let's collapse - clearUp = true; - } - - float bucketBaseArea = 0; - float bucketAdditionalArea = 0; - for (BucketPicture* current = first; current != last; current++) { - float area = current->mArea.width() * current->mArea.height(); - if (current->mBase) - bucketBaseArea += area; - else - bucketAdditionalArea += area; - } - - if (bucketBaseArea > 0 && bucketBaseArea * MAX_ADDITIONAL_AREA <= bucketAdditionalArea) { - // additional area too large, not worth maintaining - clearUp = true; - } - - // To clear things up, we just need to mark the pictures' area as empty - // We only keep the base surface. - if (clearUp) { - for (BucketPicture* current = first; current != last; current++) { - if (!current->mBase) - current->mArea.setEmpty(); - SkSafeUnref(current->mPicture); - current->mPicture = 0; - } - } - - // let's do a pass to collapse out empty areas - BucketPicture* writer = first; - for (BucketPicture* current = first; current != last; current++) { - if (current && current->mArea.isEmpty()) - continue; - *writer++ = *current; - } - - bucket->shrink(writer - first); - - // let's recompute the bases - first = bucket->begin(); - last = bucket->end(); - SkRegion drawn; - drawn.setEmpty(); - for (BucketPicture* current = first; current != last; current++) { - if (drawn.contains(current->mArea) == false) { - current->mBase = true; - } - drawn.op(current->mArea, SkRegion::kUnion_Op); - } -} - -void PictureSet::gatherBucketsForArea(WTF::Vector<Bucket*>& list, const SkIRect& rect) -{ - ALOGV("\n--- gatherBucketsForArea for rect %d, %d, %d, %d (%d x %d)", - rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, - rect.width(), rect.height()); - - if (!mBucketSizeX || !mBucketSizeY) { - ALOGD("PictureSet::gatherBucketsForArea() called with bad bucket size: x=%d y=%d", - mBucketSizeX, mBucketSizeY); - return; - } - - int x = rect.fLeft; - int y = rect.fTop; - int firstTileX = rect.fLeft / mBucketSizeX; - int firstTileY = rect.fTop / mBucketSizeY; - int lastTileX = rect.fRight / mBucketSizeX; - int lastTileY = rect.fBottom / mBucketSizeY; - - for (int i = firstTileX; i <= lastTileX; i++) { - for (int j = firstTileY; j <= lastTileY; j++) { - Bucket* bucket = getBucket(i, j); - ALOGV("gather bucket %x for %d, %d", bucket, i+1, j+1); - if (bucket) - list.append(bucket); - } - } -} - -// When we receive a new inval rect, we first find the Buckets that intersect -// with it; then we split the original inval into a serie of invals (one for -// each Bucket we intersect with). We then send that inval to the Bucket. -void PictureSet::splitAdd(const SkIRect& rect) -{ - ALOGV("\n--- splitAdd for rect %d, %d, %d, %d (%d x %d)", - rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, - rect.width(), rect.height()); - - if (!mBucketSizeX || !mBucketSizeY) { - ALOGD("PictureSet::splitAdd() called with bad bucket size: x=%d y=%d", - mBucketSizeX, mBucketSizeY); - return; - } - - // TODO: reuse gatherBucketsForArea() (change Bucket to be a class) - int x = rect.fLeft; - int y = rect.fTop; - int firstTileX = rect.fLeft / mBucketSizeX; - int firstTileY = rect.fTop / mBucketSizeY; - int lastTileX = rect.fRight / mBucketSizeX; - int lastTileY = rect.fBottom / mBucketSizeY; - - ALOGV("--- firstTile(%d, %d) lastTile(%d, %d)", - firstTileX, firstTileY, - lastTileX, lastTileY); - - for (int i = firstTileX; i <= lastTileX; i++) { - for (int j = firstTileY; j <= lastTileY; j++) { - Bucket* bucket = getBucket(i, j); - if (!bucket) - continue; - - SkIRect newRect; - int deltaX = i * mBucketSizeX; - int deltaY = j * mBucketSizeY; - int left = (i == firstTileX) ? rect.fLeft - deltaX : 0; - int top = (j == firstTileY) ? rect.fTop - deltaY : 0; - int right = (i == lastTileX) ? rect.fRight % mBucketSizeX : mBucketSizeX; - int bottom = (j == lastTileY) ? rect.fBottom % mBucketSizeY : mBucketSizeY; - - newRect.set(left, top, right, bottom); - addToBucket(bucket, deltaX, deltaY, newRect); - mUpdatedBuckets.append(bucket); - } - } - - ALOGV("--- splitAdd DONE\n"); -} - -#endif // FAST_PICTURESET - -// This function is used to maintain the list of Pictures. -// Pictures contain an SkPicture covering a specific area; some -// Pictures are "base" Pictures -- i.e. there is no Pictures -// underneath them. -// The idea here is to keep a balance between the number of Pictures -// we have (more Pictures slow us down) and the area of Pictures that -// need to be repainted (obviously, smaller areas are better). -// To do so, we try to not update/repaint the base pictures -- by -// construction, they usually cover a large area (the entire page). -// We only reset a base picture if the new invalidated area entirely -// contains it. -// Most of the time we thus work on smaller pictures on top of the -// base ones; We compute the total area of all pictures intersecting -// with the passed invalidated area (as they would need to be invalidated), -// and use that as the basis for the correct area we want to invalidate -// (we then can simply delete the pictures we intersect with). -// In addition, we do a couple of things to limit the total number of pictures -// we keep in the list: -// - if the total area of additional textures reach 65% of the base pictures, -// we delete the additional pictures and mark the base pictures as -// needing a full repaint -// - we limit the number of pictures to 32 -- above that, we do the same -// things (deleting additional pictures + full repaint of base pictures) -#ifdef FAST_PICTURESET -#else -void PictureSet::add(const SkIRect& area, uint32_t elapsed, bool split, bool empty) -{ - bool checkForNewBases = false; - - Pictures* first = mPictures.begin(); - Pictures* last = mPictures.end(); -#ifdef DEBUG - ALOGV("--- before adding the new inval ---"); - for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) { - SkIRect currentArea = working->mArea; - ALOGV("picture %d (%d, %d, %d, %d - %d x %d) base: %c", - working - first, - currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom, - currentArea.width(), currentArea.height(), - working->mBase ? 'Y' : 'N'); - } - ALOGV("----------------------------------"); -#endif - - // let's gather all the Pictures intersecting with the new invalidated - // area, collect their area and remove their picture - SkIRect totalArea = area; - for (Pictures* working = first; working != last; working++) { - SkIRect inval = area; - bool remove = false; - if (!working->mBase && SkIRect::Intersects(working->mArea, inval)) - remove = true; - if (working->mBase) { - SkIRect baseArea = working->mArea; - if (area.contains(baseArea)) { - remove = true; - checkForNewBases = true; - } - } - - if (remove) { - SkIRect currentArea = working->mArea; - if (working->mBase) - mBaseArea -= currentArea.width() * currentArea.height(); - else - mAdditionalArea -= currentArea.width() * currentArea.height(); - - totalArea.join(currentArea); - ALOGV("picture %d (%d, %d, %d, %d - %d x %d) intersects with the new inval area (%d, %d, %d, %d - %d x %d)", - working - first, - currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom, - currentArea.width(), currentArea.height(), - inval.fLeft, inval.fTop, inval.fRight, inval.fBottom, - inval.width(), inval.height()); - working->mArea.setEmpty(); - SkSafeUnref(working->mPicture); - working->mPicture = 0; -#ifdef CONTEXT_RECORDING - SkSafeUnref(working->mGraphicsOperationCollection); - working->mGraphicsOperationCollection = 0; -#endif - } - } - - // Now we can add the new Picture to the list, with the correct area - // that need to be repainted - Pictures pictureAndBounds = {totalArea, 0, -#ifdef CONTEXT_RECORDING - 0, -#endif - totalArea, elapsed, split, false, false, empty}; - -#ifdef FAST_PICTURESET - if (mPictures.size() == 0) - checkForNewBases = true; -#endif - - if (!size()) { - pictureAndBounds.mBase = true; - mBaseArea = totalArea.width() * totalArea.height(); - mAdditionalArea = 0; - mPictures.append(pictureAndBounds); - return; - } - mPictures.append(pictureAndBounds); - mAdditionalArea += totalArea.width() * totalArea.height(); - last = mPictures.end(); - first = mPictures.begin(); - - // Then, let's see if we have to clear up the pictures in order to keep - // the total number of pictures under our limit - bool clearUp = false; - if (last - first > MAX_ADDITIONAL_PICTURES) { - ALOGV("--- too many pictures, only keeping the bases : %d", last - first); - clearUp = true; - } - - if (!clearUp) { - if (mBaseArea > 0 && mBaseArea * MAX_ADDITIONAL_AREA <= mAdditionalArea) { - ALOGV("+++ the sum of the additional area is > %.2f\% of the base Area (%.2f (%.2f) <= %.2f", - MAX_ADDITIONAL_AREA * 100, mBaseArea * 0.65, mBaseArea, mAdditionalArea); - clearUp = true; - } - } - - if (clearUp) { - for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) { - if (!working->mBase) - working->mArea.setEmpty(); - SkSafeUnref(working->mPicture); - working->mPicture = 0; -#ifdef CONTEXT_RECORDING - SkSafeUnref(working->mGraphicsOperationCollection); - working->mGraphicsOperationCollection = 0; -#endif - } - } - -#ifdef DEBUG - ALOGV("--- after adding the new inval, but before collapsing ---"); - for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) { - SkIRect currentArea = working->mArea; - ALOGV("picture %d (%d, %d, %d, %d - %d x %d) base: %c", - working - first, - currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom, - currentArea.width(), currentArea.height(), - working->mBase ? 'Y' : 'N'); - } - ALOGV("----------------------------------"); - ALOGV("let's collapse..."); -#endif - - // Finally, let's do a pass to collapse out empty regions - Pictures* writer = first; - for (Pictures* working = first; working != last; working++) { - if (working && working->mArea.isEmpty()) - continue; - *writer++ = *working; - } - ALOGV("shiking of %d elements", writer - first); - mPictures.shrink(writer - first); - -#ifdef DEBUG - ALOGV("--- after adding the new inval ---"); - for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) { - SkIRect currentArea = working->mArea; - ALOGV("picture %d (%d, %d, %d, %d - %d x %d) base: %c picture %x", - working - first, - currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom, - currentArea.width(), currentArea.height(), - working->mBase ? 'Y' : 'N', working->mPicture); - } - ALOGV("----------------------------------"); -#endif - - // Base pictures might have been removed/added -- let's recompute them - SkRegion drawn; - if (checkForNewBases) { - drawn.setEmpty(); - Pictures* last = mPictures.end(); - ALOGV("checkForNewBases..."); - for (Pictures* working = mPictures.begin(); working != last; working++) { - SkRegion area; - area.setRect(working->mArea); - const SkIRect& a = area.getBounds(); - if (drawn.contains(working->mArea) == false) { - working->mBase = true; - float area = a.width() * a.height(); - mBaseArea += area; - mAdditionalArea -= area; - } - drawn.op(working->mArea, SkRegion::kUnion_Op); - } - } - -#ifdef DEBUG - ALOGV("--- after checking for bases ---"); - for (Pictures* working = mPictures.begin(); working != mPictures.end(); working++) { - SkIRect currentArea = working->mArea; - ALOGV("picture %d (%d, %d, %d, %d - %d x %d) base: %c picture %x", - working - first, - currentArea.fLeft, currentArea.fTop, currentArea.fRight, currentArea.fBottom, - currentArea.width(), currentArea.height(), - working->mBase ? 'Y' : 'N', working->mPicture); - } - ALOGV("----------------------------------"); -#endif -} -#endif // FAST_PICTURESET - -void PictureSet::setDimensions(int width, int height, SkRegion* inval) -{ - // Note that setDimensions() may be called by our ctor and should behave accordingly - if (mWidth == width && mHeight == height) - return; - DBG_SET_LOGD("%p old:(w=%d,h=%d) new:(w=%d,h=%d)", this, - mWidth, mHeight, width, height); - bool clearCache = false; - if (inval) { - if (mWidth == width && height > mHeight) { // only grew vertically - SkIRect rect; - rect.set(0, mHeight, width, height); - inval->op(rect, SkRegion::kUnion_Op); - } else { - clearCache = true; - inval->setRect(0, 0, width, height); - } - } -#ifdef FAST_PICTURESET - // First figure out how large each bucket would be if we used all of the buckets - int tmpSizeX = (width + MAX_BUCKET_COUNT_X - 1) / MAX_BUCKET_COUNT_X; - int tmpSizeY = (height + MAX_BUCKET_COUNT_Y - 1) / MAX_BUCKET_COUNT_Y; - - // Then round the bucket size up to the nearest chunk - int bucketSizeX = ((tmpSizeX - 1) / BUCKET_SIZE + 1) * BUCKET_SIZE; - int bucketSizeY = ((tmpSizeY - 1) / BUCKET_SIZE + 1) * BUCKET_SIZE; - - int bucketCountX = (width + bucketSizeX - 1) / bucketSizeX; - int bucketCountY = (height + bucketSizeY - 1) / bucketSizeY; - - // Clear the cache if the horizontal bucket count changed or the vertical - // count shrank - if (bucketCountX != mBucketCountX || bucketCountY < mBucketCountY) - clearCache = true; - - // Or if the bucket size changed - if (bucketSizeX != mBucketSizeX || bucketSizeY != mBucketSizeY) - clearCache = true; - - ALOGV("old width=%d height=%d bucketSizeX=%d bucketSizeY=%d bucketCountX=%d bucketCountY=%d clearCache=%d", - mWidth, mHeight, mBucketSizeX, mBucketSizeY, mBucketCountX, mBucketCountY, clearCache); - ALOGV("new width=%d height=%d bucketSizeX=%d bucketSizeY=%d bucketCountX=%d bucketCountY=%d clearCache=%d", - width, height, bucketSizeX, bucketSizeY, bucketCountX, bucketCountY, clearCache); -#endif - if (clearCache) - clear(); - mWidth = width; - mHeight = height; -#ifdef FAST_PICTURESET - mBucketSizeX = bucketSizeX; - mBucketSizeY = bucketSizeY; - mBucketCountX = bucketCountX; - mBucketCountY = bucketCountY; -#endif -} - -void PictureSet::clear() -{ - DBG_SET_LOG(""); -#ifdef FAST_PICTURESET - for (BucketMap::iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) { - Bucket* bucket = iter->second; - BucketPicture* first = bucket->begin(); - BucketPicture* last = bucket->end(); - for (BucketPicture* current = first; current != last; current++) { - SkSafeUnref(current->mPicture); - current->mPicture = 0; - } - bucket->clear(); - } - mBuckets.clear(); - mBucketSizeX = mBucketSizeY = BUCKET_SIZE; -#else - Pictures* last = mPictures.end(); - for (Pictures* working = mPictures.begin(); working != last; working++) { - working->mArea.setEmpty(); - SkSafeUnref(working->mPicture); -#ifdef CONTEXT_RECORDING - SkSafeUnref(working->mGraphicsOperationCollection); -#endif - } - mPictures.clear(); -#endif // FAST_PICTURESET - mWidth = mHeight = 0; -} - -uint32_t getThreadMsec() -{ -#if defined(HAVE_POSIX_CLOCKS) - struct timespec tm; - - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm); - return tm.tv_sec * 1000LL + tm.tv_nsec / 1000000; -#else - struct timeval now; - struct timezone zone; - - gettimeofday(&now, &zone); - return now.tv_sec * 1000LL + now.tv_usec / 1000; -#endif -} - -bool PictureSet::draw(SkCanvas* canvas) -{ -#ifdef FAST_PICTURESET - ALOGV("PictureSet %x draw on canvas %x", this, canvas); - SkRect bounds; - if (canvas->getClipBounds(&bounds) == false) - return false; - SkIRect irect; - bounds.roundOut(&irect); - - WTF::Vector<Bucket*> list; - gatherBucketsForArea(list, irect); - - ALOGV("PictureSet draw on canvas %x, we have %d buckets", canvas, list.size()); - for (unsigned int i = 0; i < list.size(); i++) { - Bucket* bucket = list[i]; - ALOGV("We paint using bucket %x with %d pictures", bucket, bucket->size()); - for (unsigned int j = 0; j < bucket->size(); j++) { - BucketPicture& picture = bucket->at(j); - if (!picture.mPicture) - continue; - int saved = canvas->save(); - SkRect pathBounds; - pathBounds.set(picture.mRealArea); - ALOGV("[%d/%d] draw on canvas with clip %d, %d, %d, %d - %d x %d", - j, bucket->size(), - picture.mRealArea.fLeft, - picture.mRealArea.fTop, - picture.mRealArea.fRight, - picture.mRealArea.fBottom, - picture.mRealArea.width(), - picture.mRealArea.height()); - canvas->clipRect(pathBounds); - canvas->translate(pathBounds.fLeft, pathBounds.fTop); - canvas->save(); - canvas->drawPicture(*picture.mPicture); - canvas->restoreToCount(saved); - } - } - return false; - -#else - - validate(__FUNCTION__); - Pictures* first = mPictures.begin(); - Pictures* last = mPictures.end(); - Pictures* working; - SkRect bounds; - if (canvas->getClipBounds(&bounds) == false) - return false; - SkIRect irect; - bounds.roundOut(&irect); - if (!irect.intersect(0, 0, width(), height())) - return false; - for (working = last; working != first; ) { - --working; - if (working->mArea.contains(irect)) { -#if PICTURE_SET_DEBUG - const SkIRect& b = working->mArea; - DBG_SET_LOGD("contains working->mArea={%d,%d,%d,%d}" - " irect={%d,%d,%d,%d}", b.fLeft, b.fTop, b.fRight, b.fBottom, - irect.fLeft, irect.fTop, irect.fRight, irect.fBottom); -#endif - first = working; - break; - } - } - DBG_SET_LOGD("%p first=%d last=%d", this, first - mPictures.begin(), - last - mPictures.begin()); - uint32_t maxElapsed = 0; - for (working = first; working != last; working++) { - SkRegion area; - area.setRect(working->mArea); - if (area.quickReject(irect)) { -#if PICTURE_SET_DEBUG - const SkIRect& b = area.getBounds(); - DBG_SET_LOGD("[%d] %p quickReject working->mArea={%d,%d,%d,%d}" - " irect={%d,%d,%d,%d}", working - first, working, - b.fLeft, b.fTop, b.fRight, b.fBottom, - irect.fLeft, irect.fTop, irect.fRight, irect.fBottom); -#endif - working->mElapsed = 0; - continue; - } - int saved = canvas->save(); - SkRect pathBounds; - if (area.isComplex()) { - SkPath pathClip; - area.getBoundaryPath(&pathClip); - canvas->clipPath(pathClip); - pathBounds = pathClip.getBounds(); - } else { - pathBounds.set(area.getBounds()); - canvas->clipRect(pathBounds); - } - canvas->translate(pathBounds.fLeft, pathBounds.fTop); - canvas->save(); - uint32_t startTime = getThreadMsec(); - -#ifdef CONTEXT_RECORDING - WebCore::PlatformGraphicsContextSkia context(canvas); - working->mGraphicsOperationCollection->apply(&context); -#else - canvas->drawPicture(*working->mPicture); -#endif - - size_t elapsed = working->mElapsed = getThreadMsec() - startTime; - working->mWroteElapsed = true; - if (maxElapsed < elapsed && (pathBounds.width() >= MIN_SPLITTABLE || - pathBounds.height() >= MIN_SPLITTABLE)) - maxElapsed = elapsed; - canvas->restoreToCount(saved); -#define DRAW_TEST_IMAGE 01 -#if DRAW_TEST_IMAGE && PICTURE_SET_DEBUG - SkColor color = 0x3f000000 | (0xffffff & (unsigned) working); - canvas->drawColor(color); - SkPaint paint; - color ^= 0x00ffffff; - paint.setColor(color); - char location[256]; - for (int x = area.getBounds().fLeft & ~0x3f; - x < area.getBounds().fRight; x += 0x40) { - for (int y = area.getBounds().fTop & ~0x3f; - y < area.getBounds().fBottom; y += 0x40) { - int len = snprintf(location, sizeof(location) - 1, "(%d,%d)", x, y); - canvas->drawText(location, len, x, y, paint); - } - } -#endif - DBG_SET_LOGD("[%d] %p working->mArea={%d,%d,%d,%d} elapsed=%d base=%s", - working - first, working, - area.getBounds().fLeft, area.getBounds().fTop, - area.getBounds().fRight, area.getBounds().fBottom, - working->mElapsed, working->mBase ? "true" : "false"); - } - // dump(__FUNCTION__); - return maxElapsed >= MAX_DRAW_TIME; -#endif // FAST_PICTURESET -} - -void PictureSet::dump(const char* label) const -{ -#if PICTURE_SET_DUMP - DBG_SET_LOGD("%p %s (%d) (w=%d,h=%d)", this, label, mPictures.size(), - mWidth, mHeight); - const Pictures* last = mPictures.end(); - for (const Pictures* working = mPictures.begin(); working != last; working++) { - const SkIRect& bounds = working->mArea.getBounds(); - const SkIRect& unsplit = working->mUnsplit; - MeasureStream measure; - if (working->mPicture != NULL) - working->mPicture->serialize(&measure); - ALOGD(" [%d]" - " mArea.bounds={%d,%d,r=%d,b=%d}" - " mPicture=%p" - " mUnsplit={%d,%d,r=%d,b=%d}" - " mElapsed=%d" - " mSplit=%s" - " mWroteElapsed=%s" - " mBase=%s" - " pict-size=%d", - working - mPictures.begin(), - bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, - working->mPicture, - unsplit.fLeft, unsplit.fTop, unsplit.fRight, unsplit.fBottom, - working->mElapsed, working->mSplit ? "true" : "false", - working->mWroteElapsed ? "true" : "false", - working->mBase ? "true" : "false", - measure.mTotal); - } -#endif -} - -class IsEmptyBounder : public SkBounder { - virtual bool onIRect(const SkIRect& rect) { - return false; - } -}; - -class IsEmptyCanvas : public SkCanvas { -public: - IsEmptyCanvas(SkBounder* bounder, SkPicture* picture) : - mPicture(picture), mEmpty(true) { - setBounder(bounder); - } - - void notEmpty() { - mEmpty = false; - mPicture->abortPlayback(); - } - - virtual bool clipPath(const SkPath&, SkRegion::Op) { - // this can be expensive to actually do, and doesn't affect the - // question of emptiness, so we make it a no-op - return true; - } - - virtual void commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* rect, - const SkMatrix& , const SkPaint& ) { - if (bitmap.width() <= 1 || bitmap.height() <= 1) - return; - DBG_SET_LOGD("abort {%d,%d}", bitmap.width(), bitmap.height()); - notEmpty(); - } - - virtual void drawPaint(const SkPaint& paint) { - } - - virtual void drawPath(const SkPath& , const SkPaint& paint) { - DBG_SET_LOG("abort"); - notEmpty(); - } - - virtual void drawPoints(PointMode , size_t , const SkPoint [], - const SkPaint& paint) { - } - - virtual void drawRect(const SkRect& , const SkPaint& paint) { - // wait for visual content - if (paint.getColor() != SK_ColorWHITE) - notEmpty(); - } - - virtual void drawSprite(const SkBitmap& , int , int , - const SkPaint* paint = NULL) { - DBG_SET_LOG("abort"); - notEmpty(); - } - - virtual void drawText(const void* , size_t byteLength, SkScalar , - SkScalar , const SkPaint& paint) { - DBG_SET_LOGD("abort %d", byteLength); - notEmpty(); - } - - virtual void drawPosText(const void* , size_t byteLength, - const SkPoint [], const SkPaint& paint) { - DBG_SET_LOGD("abort %d", byteLength); - notEmpty(); - } - - virtual void drawPosTextH(const void* , size_t byteLength, - const SkScalar [], SkScalar , - const SkPaint& paint) { - DBG_SET_LOGD("abort %d", byteLength); - notEmpty(); - } - - virtual void drawTextOnPath(const void* , size_t byteLength, - const SkPath& , const SkMatrix* , - const SkPaint& paint) { - DBG_SET_LOGD("abort %d", byteLength); - notEmpty(); - } - - virtual void drawPicture(SkPicture& picture) { - SkCanvas::drawPicture(picture); - } - - SkPicture* mPicture; - bool mEmpty; -}; - -bool PictureSet::emptyPicture(SkPicture* picture) const -{ - IsEmptyBounder isEmptyBounder; - IsEmptyCanvas checker(&isEmptyBounder, picture); - SkBitmap bitmap; - bitmap.setConfig(SkBitmap::kARGB_8888_Config, mWidth, mHeight); - checker.setBitmapDevice(bitmap); - checker.drawPicture(*picture); - return checker.mEmpty; -} - -bool PictureSet::isEmpty() const -{ -#ifdef FAST_PICTURESET - // For now, just assume the pictureset is *not* empty - // if the hashmap contains something - for (BucketMap::const_iterator iter = mBuckets.begin(); iter != mBuckets.end(); ++iter) { - if (iter->second->size() > 0) - return false; - } - return true; -#else - const Pictures* last = mPictures.end(); - for (const Pictures* working = mPictures.begin(); working != last; working++) { - if (!working->mEmpty) - return false; - } - return true; -#endif // FAST_PICTURESET -} - -void PictureSet::set(const PictureSet& src) -{ - DBG_SET_LOGD("start %p src=%p", this, &src); - clear(); - setDimensions(src.mWidth, src.mHeight); -#ifdef FAST_PICTURESET - ALOGV("\n--- set picture ---"); - for (BucketMap::const_iterator iter = src.mBuckets.begin(); - iter != src.mBuckets.end(); ++iter) { - Bucket* sourceBucket = iter->second; - Bucket* targetBucket = getBucket(iter->first.first-1, iter->first.second-1); - BucketPicture* first = sourceBucket->begin(); - BucketPicture* last = sourceBucket->end(); - ALOGV("set from bucket %x (%d, %d), %d pictures", sourceBucket, - iter->first.first, iter->first.second, sourceBucket->size()); - for (BucketPicture* current = first; current != last; current++) { - ALOGV("set picture %x from bucket %x in bucket %x (%d, %d)", - current->mPicture, sourceBucket, targetBucket, - iter->first.first, iter->first.second); - SkSafeRef(current->mPicture); - BucketPicture picture = { current->mPicture, current->mArea, - current->mRealArea, current->mBase }; - targetBucket->append(picture); - } - } - ALOGV("--- DONE set picture ---\n"); -#else - const Pictures* last = src.mPictures.end(); - for (const Pictures* working = src.mPictures.begin(); working != last; working++) - add(working); - // dump(__FUNCTION__); - validate(__FUNCTION__); - DBG_SET_LOG("end"); -#endif // FAST_PICTURESET -} - -#ifdef FAST_PICTURESET -#else - -void PictureSet::setDrawTimes(const PictureSet& src) -{ - validate(__FUNCTION__); - if (mWidth != src.mWidth || mHeight != src.mHeight) - return; - Pictures* last = mPictures.end(); - Pictures* working = mPictures.begin(); - if (working == last) - return; - const Pictures* srcLast = src.mPictures.end(); - const Pictures* srcWorking = src.mPictures.begin(); - for (; srcWorking != srcLast; srcWorking++) { - if (srcWorking->mWroteElapsed == false) - continue; - while ((srcWorking->mArea != working->mArea || - srcWorking->mPicture != working->mPicture)) { - if (++working == last) - return; - } - DBG_SET_LOGD("%p [%d] [%d] {%d,%d,r=%d,b=%d} working->mElapsed=%d <- %d", - this, working - mPictures.begin(), srcWorking - src.mPictures.begin(), - working->mArea.fLeft, working->mArea.fTop, - working->mArea.fRight, working->mArea.fBottom, - working->mElapsed, srcWorking->mElapsed); - working->mElapsed = srcWorking->mElapsed; - } -} - -void PictureSet::setPicture(size_t i, SkPicture* p) -{ - SkSafeUnref(mPictures[i].mPicture); - mPictures[i].mPicture = p; - mPictures[i].mEmpty = emptyPicture(p); -} - -#ifdef CONTEXT_RECORDING -void PictureSet::setGraphicsOperationCollection(size_t i, WebCore::GraphicsOperationCollection* p) -{ - SkSafeUnref(mPictures[i].mGraphicsOperationCollection); - mPictures[i].mGraphicsOperationCollection = p; -} -#endif - -void PictureSet::split(PictureSet* out) const -{ - dump(__FUNCTION__); - DBG_SET_LOGD("%p", this); - SkIRect totalBounds; - out->mWidth = mWidth; - out->mHeight = mHeight; - totalBounds.set(0, 0, mWidth, mHeight); - SkRegion* total = new SkRegion(totalBounds); - const Pictures* last = mPictures.end(); - const Pictures* working; - uint32_t balance = 0; - int multiUnsplitFastPictures = 0; // > 1 has more than 1 - for (working = mPictures.begin(); working != last; working++) { - if (working->mElapsed >= MAX_DRAW_TIME || working->mSplit) - continue; - if (++multiUnsplitFastPictures > 1) - break; - } - for (working = mPictures.begin(); working != last; working++) { - uint32_t elapsed = working->mElapsed; - if (elapsed < MAX_DRAW_TIME) { - bool split = working->mSplit; - DBG_SET_LOGD("elapsed=%d working=%p total->getBounds()=" - "{%d,%d,r=%d,b=%d} split=%s", elapsed, working, - total->getBounds().fLeft, total->getBounds().fTop, - total->getBounds().fRight, total->getBounds().fBottom, - split ? "true" : "false"); - if (multiUnsplitFastPictures <= 1 || split) { - total->op(working->mArea, SkRegion::kDifference_Op); - out->add(working->mArea, elapsed, split, - working->mEmpty); - } else if (balance < elapsed) - balance = elapsed; - continue; - } - total->op(working->mArea, SkRegion::kDifference_Op); - const SkIRect& bounds = working->mArea; - int width = bounds.width(); - int height = bounds.height(); - int across = 1; - int down = 1; - while (height >= MIN_SPLITTABLE || width >= MIN_SPLITTABLE) { - if (height >= width) { - height >>= 1; - down <<= 1; - } else { - width >>= 1; - across <<= 1 ; - } - if ((elapsed >>= 1) < MAX_DRAW_TIME) - break; - } - width = bounds.width(); - height = bounds.height(); - int top = bounds.fTop; - for (int indexY = 0; indexY < down; ) { - int bottom = bounds.fTop + height * ++indexY / down; - int left = bounds.fLeft; - for (int indexX = 0; indexX < across; ) { - int right = bounds.fLeft + width * ++indexX / across; - SkIRect cBounds; - cBounds.set(left, top, right, bottom); - out->add(cBounds, elapsed, true, - (across | down) != 1 ? false : working->mEmpty); - left = right; - } - top = bottom; - } - } - DBG_SET_LOGD("%p w=%d h=%d total->isEmpty()=%s multiUnsplitFastPictures=%d", - this, mWidth, mHeight, total->isEmpty() ? "true" : "false", - multiUnsplitFastPictures); - if (!total->isEmpty() && multiUnsplitFastPictures > 1) - out->add(*total, balance, false); - delete total; - validate(__FUNCTION__); - out->dump("split-out"); -} - -#endif // FAST_PICTURESET - -bool PictureSet::validate(const char* funct) const -{ -#ifdef FAST_PICTURESET - return true; -#else - bool valid = true; -#if PICTURE_SET_VALIDATE - SkRegion all; - const Pictures* first = mPictures.begin(); - for (const Pictures* working = mPictures.end(); working != first; ) { - --working; - const SkPicture* pict = working->mPicture; - const SkRegion& area = working->mArea; - const SkIRect& bounds = area.getBounds(); - bool localValid = false; - if (working->mUnsplit.isEmpty()) - ALOGD("%s working->mUnsplit.isEmpty()", funct); - else if (working->mUnsplit.contains(bounds) == false) - ALOGD("%s working->mUnsplit.contains(bounds) == false", funct); - else if (working->mElapsed >= 1000) - ALOGD("%s working->mElapsed >= 1000", funct); - else if ((working->mSplit & 0xfe) != 0) - ALOGD("%s (working->mSplit & 0xfe) != 0", funct); - else if ((working->mWroteElapsed & 0xfe) != 0) - ALOGD("%s (working->mWroteElapsed & 0xfe) != 0", funct); - else if (pict != NULL) { - int pictWidth = pict->width(); - int pictHeight = pict->height(); - if (pictWidth < bounds.width()) - ALOGD("%s pictWidth=%d < bounds.width()=%d", funct, pictWidth, bounds.width()); - else if (pictHeight < bounds.height()) - ALOGD("%s pictHeight=%d < bounds.height()=%d", funct, pictHeight, bounds.height()); - else if (working->mArea.isEmpty()) - ALOGD("%s working->mArea.isEmpty()", funct); - else - localValid = true; - } else - localValid = true; - working->mArea.validate(); - if (localValid == false) { - if (all.contains(area) == true) - ALOGD("%s all.contains(area) == true", funct); - else - localValid = true; - } - valid &= localValid; - all.op(area, SkRegion::kUnion_Op); - } - const SkIRect& allBounds = all.getBounds(); - if (valid) { - valid = false; - if (allBounds.width() != mWidth) - ALOGD("%s allBounds.width()=%d != mWidth=%d", funct, allBounds.width(), mWidth); - else if (allBounds.height() != mHeight) - ALOGD("%s allBounds.height()=%d != mHeight=%d", funct, allBounds.height(), mHeight); - else - valid = true; - } - while (valid == false) - ; -#endif - return valid; -#endif // FAST_PICTURESET -} - -} /* namespace android */ diff --git a/Source/WebKit/android/jni/PictureSet.h b/Source/WebKit/android/jni/PictureSet.h deleted file mode 100644 index 1e639da..0000000 --- a/Source/WebKit/android/jni/PictureSet.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2008, 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 PictureSet_h -#define PictureSet_h - -#define PICTURE_SET_DUMP 0 -#define PICTURE_SET_DEBUG 0 -#define PICTURE_SET_VALIDATE 0 - -#if PICTURE_SET_DEBUG -#define DBG_SET_LOG(message) ALOGD("%s %s", __FUNCTION__, message) -#define DBG_SET_LOGD(format, ...) ALOGD("%s " format, __FUNCTION__, __VA_ARGS__) -#define DEBUG_SET_UI_LOGD(...) ALOGD(__VA_ARGS__) -#else -#define DBG_SET_LOG(message) ((void)0) -#define DBG_SET_LOGD(format, ...) ((void)0) -#define DEBUG_SET_UI_LOGD(...) ((void)0) -#endif - -#include "jni.h" -#include "SkRegion.h" -#include <wtf/Vector.h> -#include <wtf/HashMap.h> - -#include "GraphicsOperationCollection.h" - -// #define FAST_PICTURESET // use a hierarchy of pictures -// #define CONTEXT_RECORDING // use the new PlatformGraphicsContextRecording - -class SkCanvas; -class SkPicture; -class SkIRect; - -namespace android { - -#ifdef FAST_PICTURESET - struct BucketPicture { - SkPicture* mPicture; - SkIRect mArea; - SkIRect mRealArea; - bool mBase; - }; - - typedef std::pair<int, int> BucketPosition; - typedef WTF::Vector<BucketPicture> Bucket; - typedef WTF::HashMap<BucketPosition , Bucket* > BucketMap; -#endif - - class PictureSet { - public: - PictureSet(); - PictureSet(const PictureSet& src) { set(src); } - PictureSet(SkPicture* picture); - virtual ~PictureSet(); - -#ifdef FAST_PICTURESET - void displayBucket(Bucket* bucket); - void displayBuckets(); - WTF::Vector<Bucket*>* bucketsToUpdate() { return &mUpdatedBuckets; } - Bucket* getBucket(int x, int y); - void addToBucket(Bucket* bucket, int dx, int dy, SkIRect& rect); - void gatherBucketsForArea(WTF::Vector<Bucket*>& list, const SkIRect& rect); - void splitAdd(const SkIRect& rect); -#endif - - void add(const SkRegion& area, uint32_t elapsed, bool split); - - // Update mWidth/mHeight, and adds any additional inval region - void setDimensions(int width, int height, SkRegion* inval = 0); - void clear(); - bool draw(SkCanvas* ); - static PictureSet* GetNativePictureSet(JNIEnv* env, jobject jpic); - int height() const { return mHeight; } - bool isEmpty() const; // returns true if empty or only trivial content - void set(const PictureSet& ); - -#ifdef FAST_PICTURESET -#else - void add(const SkIRect& area, uint32_t elapsed, bool split, bool empty); - const SkIRect& bounds(size_t i) const { - return mPictures[i].mArea; } - void setPicture(size_t i, SkPicture* p); -#ifdef CONTEXT_RECORDING - void setGraphicsOperationCollection(size_t i, WebCore::GraphicsOperationCollection* p); -#endif - void setDrawTimes(const PictureSet& ); - size_t size() const { return mPictures.size(); } - void split(PictureSet* result) const; - bool upToDate(size_t i) const { return mPictures[i].mPicture != NULL; } -#endif - int width() const { return mWidth; } - void dump(const char* label) const; - bool validate(const char* label) const; - private: - bool emptyPicture(SkPicture* ) const; // true if no text, images, paths - -#ifdef FAST_PICTURESET - BucketMap mBuckets; - WTF::Vector<Bucket*> mUpdatedBuckets; - int mBucketSizeX; - int mBucketSizeY; - int mBucketCountX; - int mBucketCountY; -#else - - struct Pictures { - SkIRect mArea; - SkPicture* mPicture; -#ifdef CONTEXT_RECORDING - WebCore::GraphicsOperationCollection* mGraphicsOperationCollection; -#endif - SkIRect mUnsplit; - uint32_t mElapsed; - bool mSplit : 8; - bool mWroteElapsed : 8; - bool mBase : 8; // true if nothing is drawn underneath this - bool mEmpty : 8; // true if the picture only draws white - }; - void add(const Pictures* temp); - WTF::Vector<Pictures> mPictures; -#endif - float mBaseArea; - float mAdditionalArea; - int mHeight; - int mWidth; - }; -} - -#endif diff --git a/Source/WebKit/android/jni/ViewStateSerializer.cpp b/Source/WebKit/android/jni/ViewStateSerializer.cpp index 5f2480a..02ddca6 100644 --- a/Source/WebKit/android/jni/ViewStateSerializer.cpp +++ b/Source/WebKit/android/jni/ViewStateSerializer.cpp @@ -38,7 +38,6 @@ #include "LayerAndroid.h" #include "LayerContent.h" #include "PictureLayerContent.h" -#include "PictureSet.h" #include "ScrollableLayerAndroid.h" #include "SkFlattenable.h" #include "SkPicture.h" diff --git a/Source/WebKit/android/jni/WebCoreViewBridge.h b/Source/WebKit/android/jni/WebCoreViewBridge.h index b04a0f9..cca4ca5 100644 --- a/Source/WebKit/android/jni/WebCoreViewBridge.h +++ b/Source/WebKit/android/jni/WebCoreViewBridge.h @@ -48,7 +48,7 @@ public: virtual ~WebCoreViewBridge() { } - virtual void draw(WebCore::GraphicsContext* ctx, + virtual void draw(WebCore::GraphicsContext* ctx, const WebCore::IntRect& rect) = 0; const WebCore::IntRect& getBounds() const diff --git a/Source/WebKit/android/jni/WebViewCore.cpp b/Source/WebKit/android/jni/WebViewCore.cpp index e0379a8..4e6ffc6 100644 --- a/Source/WebKit/android/jni/WebViewCore.cpp +++ b/Source/WebKit/android/jni/WebViewCore.cpp @@ -89,7 +89,7 @@ #include "Page.h" #include "PageGroup.h" #include "PictureLayerContent.h" -#include "PictureSetLayerContent.h" +#include "PicturePileLayerContent.h" #include "PlatformKeyboardEvent.h" #include "PlatformString.h" #include "PluginWidgetAndroid.h" @@ -169,17 +169,6 @@ FILE* gRenderTreeFile = 0; #include "RenderLayerCompositor.h" #endif -// In some cases, too many invalidations passed to the UI will slow us down. -// Limit ourselves to 32 rectangles, past this just send the area bounds to the UI. -// see WebViewCore::recordPictureSet(). -#define MAX_INVALIDATIONS 32 - -/* We pass this flag when recording the actual content, so that we don't spend - time actually regionizing complex path clips, when all we really want to do - is record them. - */ -#define PICT_RECORD_FLAGS SkPicture::kUsePathBoundsForClip_RecordingFlag - //////////////////////////////////////////////////////////////////////////////////////////////// namespace android { @@ -430,7 +419,6 @@ WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* m , m_scrollOffsetX(0) , m_scrollOffsetY(0) , m_mousePos(WebCore::IntPoint(0,0)) - , m_progressDone(false) , m_screenWidth(320) , m_screenHeight(240) , m_textWrapWidth(320) @@ -610,18 +598,8 @@ static bool layoutIfNeededRecursive(WebCore::Frame* f) WebCore::FrameView* v = f->view(); if (!v) return true; - - if (v->needsLayout()) - v->layout(f->tree()->parent()); - - WebCore::Frame* child = f->tree()->firstChild(); - bool success = true; - while (child) { - success &= layoutIfNeededRecursive(child); - child = child->tree()->nextSibling(); - } - - return success && !v->needsLayout(); + v->updateLayoutAndStyleIfNeededRecursive(); + return !v->needsLayout(); } WebCore::Node* WebViewCore::currentFocus() @@ -629,19 +607,16 @@ WebCore::Node* WebViewCore::currentFocus() return focusedFrame()->document()->focusedNode(); } -void WebViewCore::recordPictureSet(PictureSet* content) +void WebViewCore::recordPicturePile() { TRACE_METHOD(); // if there is no document yet, just return if (!m_mainFrame->document()) { - DBG_SET_LOG("!m_mainFrame->document()"); - return; - } - if (m_addInval.isEmpty()) { - DBG_SET_LOG("m_addInval.isEmpty()"); + ALOGV("!m_mainFrame->document()"); return; } + // Call layout to ensure that the contentWidth and contentHeight are correct // it's fine for layout to gather invalidates, but defeat sending a message // back to java to call webkitDraw, since we're already in the middle of @@ -704,6 +679,8 @@ void WebViewCore::recordPictureSet(PictureSet* content) // If the new total is larger than the content, resize the view to include // all the content. if (!contentRect.contains(total)) { + // TODO: Does this ever happen? Is this needed now that we don't flatten + // frames? // Resize the view to change the overflow clip. view->resize(total.fRight, total.fBottom); @@ -723,51 +700,15 @@ void WebViewCore::recordPictureSet(PictureSet* content) height = view->contentsHeight(); } -#if USE(ACCELERATED_COMPOSITING) - // The invals are not always correct when the content size has changed. For - // now, let's just reset the inval so that it invalidates the entire content - // -- the pictureset will be fully repainted, tiles will be marked dirty and - // will have to be repainted. - - // FIXME: the webkit invals ought to have been enough... - if (content->width() != width || content->height() != height) { - SkIRect r; - r.fLeft = 0; - r.fTop = 0; - r.fRight = width; - r.fBottom = height; - m_addInval.setRect(r); - } -#endif - - content->setDimensions(width, height, &m_addInval); + m_content.setSize(IntSize(width, height)); - // Add the current inval rects to the PictureSet, and rebuild it. - content->add(m_addInval, 0, false); - - // If we have too many invalidations, just get the area bounds - SkRegion::Iterator iterator(m_addInval); - int nbInvals = 0; - while (!iterator.done()) { - iterator.next(); - nbInvals++; - if (nbInvals > MAX_INVALIDATIONS) - break; - } - if (nbInvals > MAX_INVALIDATIONS) { - SkIRect r = m_addInval.getBounds(); - m_addInval.setRect(r); - } // Rebuild the pictureset (webkit repaint) - rebuildPictureSet(content); + m_content.updatePicturesIfNeeded(this); } void WebViewCore::clearContent() { - DBG_SET_LOG(""); - m_content.clear(); - m_addInval.setEmpty(); - m_rebuildInval.setEmpty(); + m_content.reset(); updateLocale(); } @@ -778,86 +719,51 @@ bool WebViewCore::focusBoundsChanged() return result; } -SkPicture* WebViewCore::rebuildPicture(const SkIRect& inval) +void WebViewCore::paintContents(WebCore::GraphicsContext* gc, WebCore::IntRect& dirty) { WebCore::FrameView* view = m_mainFrame->view(); - int width = view->contentsWidth(); - int height = view->contentsHeight(); - SkPicture* picture = new SkPicture(); - SkAutoPictureRecord arp(picture, width, height, PICT_RECORD_FLAGS); - SkAutoMemoryUsageProbe mup(__FUNCTION__); - SkCanvas* recordingCanvas = arp.getRecordingCanvas(); - WebCore::PlatformGraphicsContextSkia pgc(recordingCanvas); - WebCore::GraphicsContext gc(&pgc); IntPoint origin = view->minimumScrollPosition(); - WebCore::IntRect drawArea(inval.fLeft + origin.x(), inval.fTop + origin.y(), - inval.width(), inval.height()); - recordingCanvas->translate(-drawArea.x(), -drawArea.y()); - recordingCanvas->save(); - view->platformWidget()->draw(&gc, drawArea); - m_rebuildInval.op(inval, SkRegion::kUnion_Op); - DBG_SET_LOGD("m_rebuildInval={%d,%d,r=%d,b=%d}", - m_rebuildInval.getBounds().fLeft, m_rebuildInval.getBounds().fTop, - m_rebuildInval.getBounds().fRight, m_rebuildInval.getBounds().fBottom); - - return picture; + IntRect drawArea = dirty; + gc->translate(-origin.x(), -origin.y()); + drawArea.move(origin.x(), origin.y()); + view->platformWidget()->draw(gc, drawArea); } -#ifdef CONTEXT_RECORDING -GraphicsOperationCollection* WebViewCore::rebuildGraphicsOperationCollection(const SkIRect& inval) +SkCanvas* WebViewCore::createPrerenderCanvas(PrerenderedInval* prerendered) { - WebCore::FrameView* view = m_mainFrame->view(); - int width = view->contentsWidth(); - int height = view->contentsHeight(); - - IntPoint origin = view->minimumScrollPosition(); - WebCore::IntRect drawArea(inval.fLeft + origin.x(), inval.fTop + origin.y(), - inval.width(), inval.height()); - - AutoGraphicsOperationCollection autoPicture(drawArea); - view->platformWidget()->draw(autoPicture.context(), drawArea); - - m_rebuildInval.op(inval, SkRegion::kUnion_Op); - - SkSafeRef(autoPicture.picture()); - return autoPicture.picture(); -} -#endif - -void WebViewCore::rebuildPictureSet(PictureSet* pictureSet) -{ -#ifdef FAST_PICTURESET - WTF::Vector<Bucket*>* buckets = pictureSet->bucketsToUpdate(); - - for (unsigned int i = 0; i < buckets->size(); i++) { - Bucket* bucket = (*buckets)[i]; - for (unsigned int j = 0; j < bucket->size(); j++) { - BucketPicture& bucketPicture = (*bucket)[j]; - const SkIRect& inval = bucketPicture.mRealArea; - SkPicture* picture = rebuildPicture(inval); - SkSafeUnref(bucketPicture.mPicture); - bucketPicture.mPicture = picture; - } - } - buckets->clear(); -#else - size_t size = pictureSet->size(); - for (size_t index = 0; index < size; index++) { - if (pictureSet->upToDate(index)) - continue; - const SkIRect& inval = pictureSet->bounds(index); - DBG_SET_LOGD("pictSet=%p [%d] {%d,%d,w=%d,h=%d}", pictureSet, index, - inval.fLeft, inval.fTop, inval.width(), inval.height()); - pictureSet->setPicture(index, rebuildPicture(inval)); -#ifdef CONTEXT_RECORDING - pictureSet->setGraphicsOperationCollection(index, - rebuildGraphicsOperationCollection(inval)); -#endif + IntRect screen(m_scrollOffsetX, m_scrollOffsetY, m_screenWidth, m_screenHeight); + if (prerendered->area.isEmpty() || !prerendered->area.intersects(screen)) + return 0; + FloatRect scaledArea = prerendered->area; + scaledArea.scale(m_scale); + IntRect enclosingScaledArea = enclosingIntRect(scaledArea); + if (enclosingScaledArea.isEmpty()) + return 0; + prerendered->screenArea = enclosingScaledArea; + FloatRect enclosingDocArea(enclosingScaledArea); + enclosingDocArea.scale(1 / m_scale); + prerendered->area = enclosingIntRect(enclosingDocArea); + if (prerendered->area.isEmpty()) + return 0; + // TODO: We need a better heuristic for this. We should change this to: + // 1) Limit by area, not width/height (as we care more about the RAM than size) + // 2) Clip by the screen, but "round out" to make sure we cover partially + // visible tiles + int maxWidth = ceilf(m_screenWidth * m_scale); + int maxHeight = ceilf(m_screenHeight * m_scale); + if (enclosingScaledArea.width() <= maxWidth + && enclosingScaledArea.height() <= maxHeight) { + prerendered->bitmap.setConfig(SkBitmap::kARGB_8888_Config, + enclosingScaledArea.width(), + enclosingScaledArea.height()); + prerendered->bitmap.allocPixels(); + SkCanvas* bitmapCanvas = new SkCanvas(prerendered->bitmap); + bitmapCanvas->scale(m_scale, m_scale); + bitmapCanvas->translate(-enclosingDocArea.x(), -enclosingDocArea.y()); + return bitmapCanvas; } - - pictureSet->validate(__FUNCTION__); -#endif + return 0; } void WebViewCore::notifyAnimationStarted() @@ -896,7 +802,8 @@ BaseLayerAndroid* WebViewCore::createBaseLayer() bodyHasFixedBackgroundImage = style->hasFixedBackgroundImage(); } - PictureSetLayerContent* content = new PictureSetLayerContent(m_content); + PicturePileLayerContent* content = new PicturePileLayerContent(m_content); + m_content.clearPrerenders(); BaseLayerAndroid* realBase = 0; LayerAndroid* base = 0; @@ -952,46 +859,22 @@ BaseLayerAndroid* WebViewCore::createBaseLayer() BaseLayerAndroid* WebViewCore::recordContent(SkIPoint* point) { - // If there is a pending style recalculation, just return. - if (m_mainFrame->document()->isPendingStyleRecalc()) { - DBG_SET_LOG("recordContent: pending style recalc, ignoring."); - return 0; - } - float progress = (float) m_mainFrame->page()->progress()->estimatedProgress(); - m_progressDone = progress <= 0.0f || progress >= 1.0f; - recordPictureSet(&m_content); - if (!m_progressDone && m_content.isEmpty()) { - DBG_SET_LOGD("empty (progress=%g)", progress); - return 0; - } + recordPicturePile(); BaseLayerAndroid* baseLayer = createBaseLayer(); - baseLayer->markAsDirty(m_addInval); - m_addInval.setEmpty(); + baseLayer->markAsDirty(m_content.dirtyRegion()); + m_content.dirtyRegion().setEmpty(); #if USE(ACCELERATED_COMPOSITING) #else baseLayer->markAsDirty(m_rebuildInval); #endif - m_rebuildInval.setEmpty(); - point->fX = m_content.width(); - point->fY = m_content.height(); + point->fX = m_content.size().width(); + point->fY = m_content.size().height(); return baseLayer; } -void WebViewCore::splitContent(PictureSet* content) -{ -#ifdef FAST_PICTURESET -#else - bool layoutSucceeded = layoutIfNeededRecursive(m_mainFrame); - ALOG_ASSERT(layoutSucceeded, "Can never be called recursively"); - content->split(&m_content); - rebuildPictureSet(&m_content); - content->set(m_content); -#endif // FAST_PICTURESET -} - void WebViewCore::scrollTo(int x, int y, bool animate) { ALOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!"); @@ -1041,14 +924,10 @@ void WebViewCore::contentDraw() void WebViewCore::contentInvalidate(const WebCore::IntRect &r) { - DBG_SET_LOGD("rect={%d,%d,w=%d,h=%d}", r.x(), r.y(), r.width(), r.height()); - SkIRect rect(r); - if (!rect.intersect(0, 0, INT_MAX, INT_MAX)) - return; - m_addInval.op(rect, SkRegion::kUnion_Op); - DBG_SET_LOGD("m_addInval={%d,%d,r=%d,b=%d}", - m_addInval.getBounds().fLeft, m_addInval.getBounds().fTop, - m_addInval.getBounds().fRight, m_addInval.getBounds().fBottom); + IntPoint origin = m_mainFrame->view()->minimumScrollPosition(); + IntRect dirty = r; + dirty.move(-origin.x(), -origin.y()); + m_content.invalidate(dirty); if (!m_skipContentDraw) contentDraw(); } @@ -4560,13 +4439,6 @@ static jint RecordContent(JNIEnv* env, jobject obj, jint nativeClass, jobject pt return reinterpret_cast<jint>(result); } -static void SplitContent(JNIEnv* env, jobject obj, jint nativeClass, - jint content) -{ - WebViewCore* viewImpl = reinterpret_cast<WebViewCore*>(nativeClass); - viewImpl->splitContent(reinterpret_cast<PictureSet*>(content)); -} - static void SendListBoxChoice(JNIEnv* env, jobject obj, jint nativeClass, jint choice) { @@ -5074,8 +4946,6 @@ static JNINativeMethod gJavaWebViewCoreMethods[] = { (void*) RecordContent }, { "setViewportSettingsFromNative", "(I)V", (void*) SetViewportSettingsFromNative }, - { "nativeSplitContent", "(II)V", - (void*) SplitContent }, { "nativeSetBackgroundColor", "(II)V", (void*) SetBackgroundColor }, { "nativeRegisterURLSchemeAsLocal", "(ILjava/lang/String;)V", diff --git a/Source/WebKit/android/jni/WebViewCore.h b/Source/WebKit/android/jni/WebViewCore.h index 3e1dbab..8dc1d07 100644 --- a/Source/WebKit/android/jni/WebViewCore.h +++ b/Source/WebKit/android/jni/WebViewCore.h @@ -31,7 +31,7 @@ #include "FileChooser.h" #include "FocusDirection.h" #include "HitTestResult.h" -#include "PictureSet.h" +#include "PicturePile.h" #include "PlatformGraphicsContext.h" #include "Position.h" #include "ScrollTypes.h" @@ -121,7 +121,7 @@ namespace android { }; // one instance of WebViewCore per page for calling into Java's WebViewCore - class WebViewCore : public WebCoreRefObject { + class WebViewCore : public WebCoreRefObject, public WebCore::PicturePainter { public: /** * Initialize the native WebViewCore with a JNI environment, a Java @@ -526,9 +526,6 @@ namespace android { WebCore::Frame* mainFrame() const { return m_mainFrame; } WebCore::Frame* focusedFrame() const; - // utility to split slow parts of the picture set - void splitContent(PictureSet*); - void notifyWebAppCanBeInstalled(); void deleteText(int startX, int startY, int endX, int endY); @@ -625,13 +622,13 @@ namespace android { WebCore::Node* currentFocus(); // Create a set of pictures to represent the drawn DOM, driven by // the invalidated region and the time required to draw (used to draw) - void recordPictureSet(PictureSet* master); + void recordPicturePile(); - SkPicture* rebuildPicture(const SkIRect& inval); + virtual void paintContents(WebCore::GraphicsContext* gc, WebCore::IntRect& dirty); + virtual SkCanvas* createPrerenderCanvas(WebCore::PrerenderedInval* prerendered); #ifdef CONTEXT_RECORDING WebCore::GraphicsOperationCollection* rebuildGraphicsOperationCollection(const SkIRect& inval); #endif - void rebuildPictureSet(PictureSet* ); void sendNotifyProgressFinished(); /* * Handle a mouse click, either from a touch or trackball press. @@ -750,9 +747,7 @@ namespace android { struct TextFieldInitDataGlue* m_textFieldInitDataGlue; WebCore::Frame* m_mainFrame; WebCoreReply* m_popupReply; - PictureSet m_content; // the set of pictures to draw - SkRegion m_addInval; // the accumulated inval region (not yet drawn) - SkRegion m_rebuildInval; // the accumulated region for rebuilt pictures + WebCore::PicturePile m_content; // the set of pictures to draw // Used in passToJS to avoid updating the UI text field until after the // key event has been processed. bool m_blockTextfieldUpdates; @@ -766,7 +761,6 @@ namespace android { int m_scrollOffsetX; // webview.java's current scroll in X int m_scrollOffsetY; // webview.java's current scroll in Y WebCore::IntPoint m_mousePos; - bool m_progressDone; int m_screenWidth; // width of the visible rect in document coordinates int m_screenHeight;// height of the visible rect in document coordinates int m_textWrapWidth; diff --git a/Source/WebKit/android/nav/WebView.cpp b/Source/WebKit/android/nav/WebView.cpp index 51ffdfe..9d4d7a4 100644 --- a/Source/WebKit/android/nav/WebView.cpp +++ b/Source/WebKit/android/nav/WebView.cpp @@ -293,12 +293,11 @@ int drawGL(WebCore::IntRect& viewRect, WebCore::IntRect* invalRect, return 0; } -PictureSet* draw(SkCanvas* canvas, SkColor bgColor, DrawExtras extras, bool split) +void draw(SkCanvas* canvas, SkColor bgColor, DrawExtras extras) { - PictureSet* ret = 0; if (!m_baseLayer) { canvas->drawColor(bgColor); - return ret; + return; } // draw the content of the base layer first @@ -327,8 +326,6 @@ PictureSet* draw(SkCanvas* canvas, SkColor bgColor, DrawExtras extras, bool spli m_baseLayer->setMatrix(canvas->getTotalMatrix()); canvas->resetMatrix(); m_baseLayer->draw(canvas, getDrawExtra(extras)); - - return ret; } int getScaledMaxXScroll() @@ -543,14 +540,6 @@ bool setBaseLayer(BaseLayerAndroid* newBaseLayer, bool showVisualIndicator, return queueFull; } -void replaceBaseContent(PictureSet* set) -{ - if (!m_baseLayer) - return; - // TODO: remove the split picture codepath - delete set; -} - void copyBaseContentToPicture(SkPicture* picture) { if (!m_baseLayer) @@ -805,16 +794,14 @@ static SkRect jrectf_to_rect(JNIEnv* env, jobject obj) return rect; } -static jint nativeDraw(JNIEnv *env, jobject obj, jobject canv, +static void nativeDraw(JNIEnv *env, jobject obj, jobject canv, jobject visible, jint color, - jint extras, jboolean split) { + jint extras) { SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv); WebView* webView = GET_NATIVE_VIEW(env, obj); SkRect visibleRect = jrectf_to_rect(env, visible); webView->setVisibleRect(visibleRect); - PictureSet* pictureSet = webView->draw(canvas, color, - static_cast<WebView::DrawExtras>(extras), split); - return reinterpret_cast<jint>(pictureSet); + webView->draw(canvas, color, static_cast<WebView::DrawExtras>(extras)); } static jint nativeCreateDrawGLFunction(JNIEnv *env, jobject obj, jint nativeView, @@ -898,12 +885,6 @@ static BaseLayerAndroid* nativeGetBaseLayer(JNIEnv *env, jobject obj) return GET_NATIVE_VIEW(env, obj)->getBaseLayer(); } -static void nativeReplaceBaseContent(JNIEnv *env, jobject obj, jint content) -{ - PictureSet* set = reinterpret_cast<PictureSet*>(content); - GET_NATIVE_VIEW(env, obj)->replaceBaseContent(set); -} - static void nativeCopyBaseContentToPicture(JNIEnv *env, jobject obj, jobject pict) { SkPicture* picture = GraphicsJNI::getNativePicture(env, pict); @@ -1110,7 +1091,7 @@ static void nativeDumpDisplayTree(JNIEnv* env, jobject jwebview, jstring jurl) SkDumpCanvas canvas(&dumper); // this will playback the picture into the canvas, which will // spew its contents to the dumper - view->draw(&canvas, 0, WebView::DrawExtrasNone, false); + view->draw(&canvas, 0, WebView::DrawExtrasNone); // we're done with the file now fwrite("\n", 1, 1, file); fclose(file); @@ -1249,7 +1230,7 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeCreate }, { "nativeDestroy", "()V", (void*) nativeDestroy }, - { "nativeDraw", "(Landroid/graphics/Canvas;Landroid/graphics/RectF;IIZ)I", + { "nativeDraw", "(Landroid/graphics/Canvas;Landroid/graphics/RectF;II)V", (void*) nativeDraw }, { "nativeCreateDrawGLFunction", "(ILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/RectF;FI)I", (void*) nativeCreateDrawGLFunction }, @@ -1271,8 +1252,6 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeSetBaseLayer }, { "nativeGetBaseLayer", "()I", (void*) nativeGetBaseLayer }, - { "nativeReplaceBaseContent", "(I)V", - (void*) nativeReplaceBaseContent }, { "nativeCopyBaseContentToPicture", "(Landroid/graphics/Picture;)V", (void*) nativeCopyBaseContentToPicture }, { "nativeHasContent", "()Z", |