platform/graphics/android/MediaLayer.cpp \
platform/graphics/android/MediaTexture.cpp \
platform/graphics/android/PaintLayerOperation.cpp \
+ platform/graphics/android/PaintTileOperation.cpp \
platform/graphics/android/PathAndroid.cpp \
platform/graphics/android/PatternAndroid.cpp \
platform/graphics/android/PlatformGraphicsContext.cpp \
platform/graphics/android/TexturesGenerator.cpp \
platform/graphics/android/TilesManager.cpp \
platform/graphics/android/TiledPage.cpp \
- platform/graphics/android/TileSet.cpp \
platform/graphics/android/VideoLayerAndroid.cpp \
platform/graphics/android/android_graphics.cpp \
// so that we do not slow down zooming unnecessarily.
if (m_glWebViewState->currentScale() != scale
&& (m_glWebViewState->scaleRequestState() == GLWebViewState::kNoScaleRequest
- || m_glWebViewState->scaleRequestState() == GLWebViewState::kWillScheduleRequest
- || m_glWebViewState->futureScale() != scale)) {
+ || m_glWebViewState->futureScale() != scale)
+ || m_glWebViewState->scaleRequestState() == GLWebViewState::kWillScheduleRequest) {
// schedule the new request
m_glWebViewState->scheduleUpdate(currentTime, viewportTileBounds, scale);
zooming = true;
}
+ // Display the current page
+ TiledPage* tiledPage = m_glWebViewState->frontPage();
+ tiledPage->setScale(m_glWebViewState->currentScale());
+
// Let's prepare the page if needed
if (prepareNextTiledPage) {
TiledPage* nextTiledPage = m_glWebViewState->backPage();
nextTiledPage->setScale(scale);
m_glWebViewState->setFutureViewport(viewportTileBounds);
m_glWebViewState->lockBaseLayerUpdate();
- nextTiledPage->prepare(goingDown, goingLeft, viewportTileBounds, true);
+ nextTiledPage->prepare(goingDown, goingLeft, viewportTileBounds);
+ // Cancel pending paints for the foreground page
+ TilesManager::instance()->removePaintOperationsForPage(tiledPage, false);
}
float transparency = 1;
}
}
- // Display the current page
- TiledPage* tiledPage = m_glWebViewState->frontPage();
- tiledPage->setScale(m_glWebViewState->currentScale());
const SkIRect& preZoomBounds = m_glWebViewState->preZoomBounds();
TiledPage* nextTiledPage = m_glWebViewState->backPage();
if (!zooming)
m_glWebViewState->unlockBaseLayerUpdate();
- tiledPage->prepare(goingDown, goingLeft, preZoomBounds, true);
+ if (!prepareNextTiledPage)
+ tiledPage->prepare(goingDown, goingLeft, preZoomBounds);
tiledPage->draw(transparency, preZoomBounds);
}
, m_texture(0)
, m_scale(1)
, m_dirty(true)
+ , m_repaintPending(false)
, m_usable(true)
, m_lastDirtyPicture(0)
, m_fullRepaintA(true)
, m_fullRepaintB(true)
- , m_painting(false)
, m_lastPaintedPicture(0)
{
#ifdef DEBUG_COUNT
BackedDoubleBufferedTexture* texture = TilesManager::instance()->getAvailableTexture(this);
android::AutoMutex lock(m_atomicSync);
- if (texture && !m_painting &&
- m_texture != texture) {
+ if (texture && m_texture != texture) {
m_lastPaintedPicture = 0;
fullInval();
m_texture = texture;
XLOG("%x removeTexture res: %x... page %x", this, m_texture, m_page);
// We update atomically, so paintBitmap() can see the correct value
android::AutoMutex lock(m_atomicSync);
- if (m_painting)
- return false;
if (m_texture == texture)
m_texture = 0;
return true;
return m_dirty;
}
+bool BaseTile::isRepaintPending()
+{
+ android::AutoMutex lock(m_atomicSync);
+ return m_repaintPending;
+}
+
+void BaseTile::setRepaintPending(bool pending)
+{
+ android::AutoMutex lock(m_atomicSync);
+ m_repaintPending = pending;
+}
+
void BaseTile::setUsedLevel(int usedLevel)
{
if (m_texture)
m_texture->setUsedLevel(usedLevel);
}
+int BaseTile::usedLevel()
+{
+ if (m_texture)
+ return m_texture->usedLevel();
+ return -1;
+}
+
void BaseTile::draw(float transparency, SkRect& rect, float scale)
{
if (m_x < 0 || m_y < 0 || m_scale != scale)
bool dirty = m_dirty;
BackedDoubleBufferedTexture* texture = m_texture;
SkRegion dirtyArea = *m_currentDirtyArea;
- m_painting = true;
float scale = m_scale;
const int x = m_x;
const int y = m_y;
m_atomicSync.unlock();
if (!dirty || !texture) {
- m_painting = false;
return;
}
// transferred to another BaseTile under us)
if (texture->owner() != this || texture->usedLevel() > 1) {
texture->producerRelease();
- m_painting = false;
return;
}
texture->setTile(textureInfo, x, y, scale, pictureCount);
texture->producerReleaseAndSwap();
- m_lastPaintedPicture = pictureCount;
-
- // set the fullrepaint flags
+ if (texture == m_texture) {
+ m_lastPaintedPicture = pictureCount;
- if ((m_currentDirtyArea == &m_dirtyAreaA) && m_fullRepaintA)
- m_fullRepaintA = false;
+ // set the fullrepaint flags
- if ((m_currentDirtyArea == &m_dirtyAreaB) && m_fullRepaintB)
- m_fullRepaintB = false;
+ if ((m_currentDirtyArea == &m_dirtyAreaA) && m_fullRepaintA)
+ m_fullRepaintA = false;
- // The various checks to see if we are still dirty...
+ if ((m_currentDirtyArea == &m_dirtyAreaB) && m_fullRepaintB)
+ m_fullRepaintB = false;
- m_dirty = false;
+ // The various checks to see if we are still dirty...
- if (m_scale != scale)
- m_dirty = true;
+ m_dirty = false;
- if (!fullRepaint)
- m_currentDirtyArea->op(dirtyArea, SkRegion::kDifference_Op);
+ if (m_scale != scale)
+ m_dirty = true;
- if (!m_currentDirtyArea->isEmpty())
- m_dirty = true;
+ if (!fullRepaint)
+ m_currentDirtyArea->op(dirtyArea, SkRegion::kDifference_Op);
- // Now we can swap the dirty areas
+ if (!m_currentDirtyArea->isEmpty())
+ m_dirty = true;
- m_currentDirtyArea = m_currentDirtyArea == &m_dirtyAreaA ? &m_dirtyAreaB : &m_dirtyAreaA;
+ // Now we can swap the dirty areas
- if (!m_currentDirtyArea->isEmpty())
- m_dirty = true;
+ m_currentDirtyArea = m_currentDirtyArea == &m_dirtyAreaA ? &m_dirtyAreaB : &m_dirtyAreaA;
- if (!m_dirty)
- m_usable = true;
+ if (!m_currentDirtyArea->isEmpty())
+ m_dirty = true;
- m_painting = false;
+ if (!m_dirty)
+ m_usable = true;
+ }
m_atomicSync.unlock();
}
void reserveTexture();
void setUsedLevel(int);
+ int usedLevel();
bool isTileReady();
void draw(float transparency, SkRect& rect, float scale);
void markAsDirty(const unsigned int pictureCount,
const SkRegion& dirtyArea);
bool isDirty();
+ bool isRepaintPending();
+ void setRepaintPending(bool pending);
void setUsable(bool usable);
float scale() const { return m_scale; }
void setScale(float scale);
float m_scale;
// used to signal that the that the tile is out-of-date and needs to be redrawn
bool m_dirty;
+ // used to signal that a repaint is pending
+ bool m_repaintPending;
// used to signal whether or not the draw can use this tile.
bool m_usable;
// stores the id of the latest picture from webkit that caused this tile to
bool m_fullRepaintA;
bool m_fullRepaintB;
SkRegion* m_currentDirtyArea;
- bool m_painting;
// stores the id of the latest picture painted to the tile. If the id is 0
// then we know that the picture has not yet been painted an there is nothing
m_needsNotifyClient(false),
m_haveContents(false),
m_haveImage(false),
+ m_newImage(false),
+ m_imageRef(0),
m_foregroundLayer(0),
m_foregroundClipLayer(0)
{
return true;
}
+ if (m_needsRepaint && m_haveImage && m_newImage) {
+ // We need to tell the GL thread that we will need to repaint the
+ // texture. Only do so if we effectively have a new image!
+ m_contentLayer->needsRepaint();
+ m_newImage = false;
+ m_needsRepaint = false;
+ return true;
+ }
return false;
}
if (image) {
m_haveContents = true;
m_haveImage = true;
- m_contentLayer->setContentsImage(image->nativeImageForCurrentFrame());
- setNeedsDisplay();
- askForSync();
+ // Only pass the new image if it's a different one
+ if (image->nativeImageForCurrentFrame() != m_imageRef) {
+ m_newImage = true;
+ m_contentLayer->setContentsImage(image->nativeImageForCurrentFrame());
+ // remember the passed image.
+ m_imageRef = image->nativeImageForCurrentFrame();
+ setNeedsDisplay();
+ askForSync();
+ }
}
}
#include "GraphicsLayerClient.h"
#include "LayerAndroid.h"
#include "RefPtr.h"
+#include "SkBitmapRef.h"
#include "Vector.h"
class FloatPoint3D;
class Image;
+class SkBitmapRef;
namespace WebCore {
bool m_haveContents;
bool m_haveImage;
+ bool m_newImage;
+ SkBitmapRef* m_imageRef; // only used to remember previously passed images
Vector<FloatRect> m_invalidatedRects;
LayerAndroid::LayerAndroid(const LayerAndroid& layer) : SkLayer(layer),
m_haveClip(layer.m_haveClip),
m_isIframe(layer.m_isIframe),
+ m_contentsImage(0),
m_extra(0), // deliberately not copied
m_uniqueId(layer.m_uniqueId),
m_drawingTexture(0),
m_owningLayer(layer.m_owningLayer)
{
m_isFixed = layer.m_isFixed;
- m_contentsImage = layer.m_contentsImage;
- SkSafeRef(m_contentsImage);
+ copyBitmap(layer.m_contentsImage);
m_renderLayerPos = layer.m_renderLayerPos;
m_transform = layer.m_transform;
m_backgroundColor = layer.m_backgroundColor;
removeTexture(0);
removeChildren();
delete m_extra;
- SkSafeUnref(m_contentsImage);
+ delete m_contentsImage;
SkSafeUnref(m_recordingPicture);
m_animations.clear();
#ifdef DEBUG_COUNT
this->getChild(i)->updateGLPositions(localMatrix, drawClip(), opacity);
}
+void LayerAndroid::copyBitmap(SkBitmap* bitmap)
+{
+ if (!bitmap)
+ return;
+
+ delete m_contentsImage;
+ m_contentsImage = new SkBitmap();
+ SkBitmap::Config config = bitmap->config();
+ int w = bitmap->width();
+ int h = bitmap->height();
+ m_contentsImage->setConfig(config, w, h);
+ bitmap->copyTo(m_contentsImage, config);
+}
+
void LayerAndroid::setContentsImage(SkBitmapRef* img)
{
- SkRefCnt_SafeAssign(m_contentsImage, img);
+ copyBitmap(&img->bitmap());
}
bool LayerAndroid::needsTexture()
uniqueId(), this, m_dirty, m_reservedTexture,
m_reservedTexture->rect().width(), m_reservedTexture->rect().height());
PaintLayerOperation* operation = new PaintLayerOperation(this);
- TilesManager::instance()->scheduleOperation(operation, !m_drawingTexture);
+ TilesManager::instance()->scheduleOperation(operation);
} else {
XLOG("We don't schedule a paint for layer %d (%x), because we already sent a request",
uniqueId(), this);
if (m_drawingTexture) {
TextureInfo* textureInfo = m_drawingTexture->consumerLock();
- if (!m_drawingTexture->readyFor(this))
- m_dirty = true;
- if (textureInfo) {
+ bool ready = m_drawingTexture->readyFor(this);
+ if (textureInfo && (!m_contentsImage || (ready && m_contentsImage))) {
SkRect bounds;
bounds.set(m_drawingTexture->rect());
XLOG("LayerAndroid %d %x (%.2f, %.2f) drawGL (texture %x, %d, %d, %d, %d)",
textureInfo->m_textureId,
m_drawOpacity, true);
}
+ if (!ready)
+ m_dirty = true;
m_drawingTexture->consumerRelease();
} else if (needsTexture()) {
m_dirty = true;
IntRect textureRect = texture->rect();
canvas->drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode);
- SkPicture picture;
- SkCanvas* nCanvas = picture.beginRecording(textureRect.width(),
- textureRect.height());
- nCanvas->scale(scale, scale);
- nCanvas->translate(-textureRect.x(), -textureRect.y());
- contentDraw(nCanvas);
- picture.endRecording();
- picture.draw(canvas);
+ if (m_contentsImage) {
+ contentDraw(canvas);
+ } else {
+ SkPicture picture;
+ SkCanvas* nCanvas = picture.beginRecording(textureRect.width(),
+ textureRect.height());
+ nCanvas->scale(scale, scale);
+ nCanvas->translate(-textureRect.x(), -textureRect.y());
+ contentDraw(nCanvas);
+ picture.endRecording();
+ picture.draw(canvas);
+ }
extraDraw(canvas);
m_atomicSync.lock();
if (m_contentsImage) {
SkRect dest;
dest.set(0, 0, getSize().width(), getSize().height());
- canvas->drawBitmapRect(m_contentsImage->bitmap(), 0, dest);
+ canvas->drawBitmapRect(*m_contentsImage, 0, dest);
} else {
canvas->drawPicture(*m_recordingPicture);
}
*/
void setContentsImage(SkBitmapRef* img);
bool hasContentsImage() { return m_contentsImage; }
+ void copyBitmap(SkBitmap*);
void bounds(SkRect*) const;
// it is a much faster method than using m_recordingPicture.
SkPicture* m_recordingPicture;
- SkBitmapRef* m_contentsImage;
+ SkBitmap* m_contentsImage;
typedef HashMap<pair<String, int>, RefPtr<AndroidAnimation> > KeyframesMap;
KeyframesMap m_animations;
/*
- * Copyright 2010, The Android Open Source Project
+ * Copyright 2011, The Android Open Source Project
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
*/
#include "config.h"
-#include "TileSet.h"
-
-#if USE(ACCELERATED_COMPOSITING)
-
-#include "ClassTracker.h"
-#include "TilesManager.h"
-
-#ifdef DEBUG
-
-#include <cutils/log.h>
-#include <wtf/CurrentTime.h>
-#include <wtf/text/CString.h>
-
-#undef XLOG
-#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "TileSet", __VA_ARGS__)
-
-#else
-
-#undef XLOG
-#define XLOG(...)
-
-#endif // DEBUG
+#include "PaintTileOperation.h"
namespace WebCore {
-TileSet::TileSet(TiledPage* tiledPage, int rows, int cols)
- : m_tiledPage(tiledPage)
- , m_nbRows(rows)
- , m_nbCols(cols)
+PaintTileOperation::PaintTileOperation(BaseTile* tile)
+ : QueuedOperation(QueuedOperation::PaintTile, tile->page())
+ , m_tile(tile)
{
-#ifdef DEBUG_COUNT
- ClassTracker::instance()->increment("TileSet");
-#endif
+ if (m_tile)
+ m_tile->setRepaintPending(true);
}
-TileSet::~TileSet()
+PaintTileOperation::~PaintTileOperation()
{
-#ifdef DEBUG_COUNT
- ClassTracker::instance()->decrement("TileSet");
-#endif
+ if (m_tile) {
+ m_tile->setRepaintPending(false);
+ m_tile = 0;
+ }
}
-bool TileSet::operator==(const TileSet& set)
+bool PaintTileOperation::operator==(const QueuedOperation* operation)
{
- return m_tiledPage == set.m_tiledPage
- && m_nbRows == set.m_nbRows
- && m_nbCols == set.m_nbCols;
+ if (operation->type() != type())
+ return false;
+ const PaintTileOperation* op = static_cast<const PaintTileOperation*>(operation);
+ return op->m_tile == m_tile;
}
-
-void TileSet::paint()
+void PaintTileOperation::run()
{
- XLOG("%x, painting %d tiles", this, m_tiles.size());
- for (unsigned int i = 0; i < m_tiles.size(); i++)
- m_tiles[i]->paintBitmap();
- XLOG("%x, end of painting %d tiles", this, m_tiles.size());
+ if (m_tile) {
+ m_tile->paintBitmap();
+ m_tile->setRepaintPending(false);
+ m_tile = 0;
+ }
}
-} // namespace WebCore
+int PaintTileOperation::priority()
+{
+ if (!m_tile || m_tile->usedLevel() < 0)
+ return -1;
+ bool goingDown = m_tile->page()->scrollingDown();
+ SkIRect *rect = m_tile->page()->expandedTileBounds();
+ int firstTileX = rect->fLeft;
+ int nbTilesWidth = rect->width();
+ int priority = m_tile->x() - firstTileX;
+ if (goingDown)
+ priority += (rect->fBottom - m_tile->y()) * nbTilesWidth;
+ else
+ priority += (m_tile->y() - rect->fTop) * nbTilesWidth;
+ priority += m_tile->usedLevel() * 100000;
+ return priority;
+}
-#endif // USE(ACCELERATED_COMPOSITING)
+}
#define PaintTileSetOperation_h
#include "QueuedOperation.h"
-#include "TileSet.h"
namespace WebCore {
-class PaintTileSetOperation : public QueuedOperation {
+class PaintTileOperation : public QueuedOperation {
public:
- PaintTileSetOperation(TileSet* set)
- : QueuedOperation(QueuedOperation::PaintTileSet, set->page())
- , m_set(set) {}
- virtual ~PaintTileSetOperation()
- {
- delete m_set;
- }
- virtual bool operator==(const QueuedOperation* operation)
- {
- if (operation->type() != type())
- return false;
- const PaintTileSetOperation* op = static_cast<const PaintTileSetOperation*>(operation);
- return op->m_set == m_set;
- }
- virtual void run()
- {
- if (m_set)
- m_set->paint();
- }
+ PaintTileOperation(BaseTile* tile);
+ virtual ~PaintTileOperation();
+ virtual bool operator==(const QueuedOperation* operation);
+ virtual void run();
+ virtual int priority();
+
private:
- TileSet* m_set;
+ BaseTile* m_tile;
};
}
class QueuedOperation {
public:
- enum OperationType { Undefined, PaintTileSet, PaintLayer, DeleteTexture };
+ enum OperationType { Undefined, PaintTile, PaintLayer, DeleteTexture };
QueuedOperation(OperationType type, TiledPage* page)
: m_type(type)
, m_page(page) {}
virtual ~QueuedOperation() {}
virtual void run() = 0;
virtual bool operator==(const QueuedOperation* operation) = 0;
+ virtual int priority() { return -1; }
OperationType type() const { return m_type; }
TiledPage* page() const { return m_page; }
private:
TiledPage* m_page;
};
+class PagePaintFilter : public OperationFilter {
+ public:
+ PagePaintFilter(TiledPage* page) : m_page(page) {}
+ virtual bool check(QueuedOperation* operation)
+ {
+ if (operation->type() == QueuedOperation::PaintTile
+ && operation->page() == m_page)
+ return true;
+ return false;
+ }
+ private:
+ TiledPage* m_page;
+};
+
}
#endif // QueuedOperation_h
namespace WebCore {
-void TexturesGenerator::scheduleOperation(QueuedOperation* operation, bool scheduleFirst)
+void TexturesGenerator::scheduleOperation(QueuedOperation* operation)
{
{
android::Mutex::Autolock lock(mRequestedOperationsLock);
- for (unsigned int i = 0; i < mRequestedOperations.size(); i++) {
- QueuedOperation** s = &mRequestedOperations[i];
- // A similar operation is already in the queue. The newer operation may
- // have additional dirty tiles so delete the existing operation and
- // replace it with the new one.
- if (*s && *s == operation) {
- QueuedOperation* oldOperation = *s;
- *s = operation;
- delete oldOperation;
- return;
- }
- }
-
- if (scheduleFirst)
- mRequestedOperations.prepend(operation);
- else
- mRequestedOperations.append(operation);
+ mRequestedOperations.append(operation);
}
mRequestedOperationsCond.signal();
}
removeOperationsForFilter(new PageFilter(page));
}
+void TexturesGenerator::removePaintOperationsForPage(TiledPage* page, bool waitForRunning)
+{
+ removeOperationsForFilter(new PagePaintFilter(page), waitForRunning);
+}
+
void TexturesGenerator::removeOperationsForBaseLayer(BaseLayerAndroid* layer)
{
removeOperationsForFilter(new PaintLayerBaseFilter(layer));
void TexturesGenerator::removeOperationsForFilter(OperationFilter* filter)
{
+ removeOperationsForFilter(filter, true);
+}
+
+void TexturesGenerator::removeOperationsForFilter(OperationFilter* filter, bool waitForRunning)
+{
android::Mutex::Autolock lock(mRequestedOperationsLock);
for (unsigned int i = 0; i < mRequestedOperations.size();) {
QueuedOperation* operation = mRequestedOperations[i];
}
}
- QueuedOperation* operation = m_currentOperation;
- if (operation && filter->check(operation))
- m_waitForCompletion = true;
-
- delete filter;
-
- // At this point, it means that we are currently executing an operation that
- // we want to be removed -- we should wait until it is done, so that
- // when we return our caller can be sure that there is no more operations
- // in the queue matching the given filter.
- while (m_waitForCompletion)
- mRequestedOperationsCond.wait(mRequestedOperationsLock);
+ if (waitForRunning) {
+ QueuedOperation* operation = m_currentOperation;
+ if (operation && filter->check(operation))
+ m_waitForCompletion = true;
+
+ delete filter;
+
+ // At this point, it means that we are currently executing an operation that
+ // we want to be removed -- we should wait until it is done, so that
+ // when we return our caller can be sure that there is no more operations
+ // in the queue matching the given filter.
+ while (m_waitForCompletion)
+ mRequestedOperationsCond.wait(mRequestedOperationsLock);
+ } else {
+ delete filter;
+ }
}
status_t TexturesGenerator::readyToRun()
return NO_ERROR;
}
+// Must be called from within a lock!
+QueuedOperation* TexturesGenerator::popNext()
+{
+ // Priority can change between when it was added and now
+ // Hence why the entire queue is rescanned
+ QueuedOperation* current = mRequestedOperations.last();
+ int currentPriority = current->priority();
+ if (currentPriority < 0) {
+ mRequestedOperations.removeLast();
+ return current;
+ }
+ int currentIndex = mRequestedOperations.size() - 1;
+ // Scan from the back to make removing faster (less items to copy)
+ for (int i = mRequestedOperations.size() - 2; i >= 0; i--) {
+ QueuedOperation *next = mRequestedOperations[i];
+ int nextPriority = next->priority();
+ if (nextPriority < 0) {
+ // Found a very high priority item, go ahead and just handle it now
+ mRequestedOperations.remove(i);
+ return next;
+ }
+ if (nextPriority < currentPriority) {
+ current = next;
+ currentPriority = nextPriority;
+ currentIndex = i;
+ }
+ }
+ mRequestedOperations.remove(currentIndex);
+ return current;
+}
+
bool TexturesGenerator::threadLoop()
{
// Check if we have any pending operations.
m_currentOperation = 0;
bool stop = false;
while (!stop) {
- XLOG("threadLoop evaluating the requests");
mRequestedOperationsLock.lock();
- if (mRequestedOperations.size()) {
- m_currentOperation = mRequestedOperations.first();
- mRequestedOperations.remove(0);
- XLOG("threadLoop, popping the first request (%d requests left)",
- mRequestedOperations.size());
- }
+ if (mRequestedOperations.size())
+ m_currentOperation = popNext();
mRequestedOperationsLock.unlock();
if (m_currentOperation) {
- XLOG("threadLoop, painting the request");
+ XLOG("threadLoop, painting the request with priority %d", m_currentOperation->priority());
m_currentOperation->run();
- XLOG("threadLoop, painting the request - DONE");
}
mRequestedOperationsLock.lock();
mRequestedOperationsLock.unlock();
}
+ XLOG("threadLoop empty");
return true;
}
#include "LayerTexture.h"
#include "QueuedOperation.h"
-#include "TileSet.h"
#include "TiledPage.h"
#include <utils/threads.h>
void removeOperationsForPage(TiledPage* page);
void removeOperationsForBaseLayer(BaseLayerAndroid* layer);
void removeOperationsForTexture(LayerTexture* texture);
+ void removePaintOperationsForPage(TiledPage* page, bool waitForRunning);
void removeOperationsForFilter(OperationFilter* filter);
+ void removeOperationsForFilter(OperationFilter* filter, bool waitForRunning);
- void scheduleOperation(QueuedOperation* operation, bool scheduleFirst);
+ void scheduleOperation(QueuedOperation* operation);
private:
+ QueuedOperation* popNext();
virtual bool threadLoop();
Vector<QueuedOperation*> mRequestedOperations;
android::Mutex mRequestedOperationsLock;
+++ /dev/null
-/*
- * Copyright 2010, 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 TileSet_h
-#define TileSet_h
-
-#if USE(ACCELERATED_COMPOSITING)
-
-#include "BaseTile.h"
-#include "Vector.h"
-
-namespace WebCore {
-
-/**
- * This purpose of this class is to act as a container for BaseTiles that need
- * to upload their contents to the GPU. A TiledPage creates a new TileSet and
- * provides the set with identifying characteristics of the TiledPage's current
- * state (see constructor). This information allows the consumer of the TileSet
- * to determine if an equivalent TileSet already exists in the upload pipeline.
- */
-class TileSet {
-public:
- TileSet(TiledPage* tiledPage, int nbRows, int nbCols);
- ~TileSet();
-
- bool operator==(const TileSet& set);
- void paint();
-
- void add(BaseTile* texture)
- {
- m_tiles.append(texture);
- }
-
- TiledPage* page()
- {
- return m_tiledPage;
- }
-
- unsigned int size()
- {
- return m_tiles.size();
- }
-
-private:
- Vector<BaseTile*> m_tiles;
-
- TiledPage* m_tiledPage;
- int m_nbRows;
- int m_nbCols;
-};
-
-} // namespace WebCore
-
-#endif // USE(ACCELERATED_COMPOSITING)
-#endif // TileSet_h
#include "GLUtils.h"
#include "IntRect.h"
-#include "PaintTileSetOperation.h"
+#include "PaintTileOperation.h"
#include "TilesManager.h"
#ifdef DEBUG
int baseTileSize = TilesManager::instance()->maxTextureCount() + 1;
if (baseTileSize > m_baseTileSize)
m_baseTileSize = baseTileSize;
- XLOG("Allocate %d tiles", m_baseTileSize);
}
TiledPage::~TiledPage()
const int lastDirtyTileX = static_cast<int>(ceilf(inval.right() * invTileContentWidth));
const int lastDirtyTileY = static_cast<int>(ceilf(inval.bottom() * invTileContentHeight));
+ XLOG("Marking X %d-%d and Y %d-%d dirty", firstDirtyTileX, lastDirtyTileX, firstDirtyTileY, lastDirtyTileY);
// We defer marking the tile as dirty until the next time we need to prepare
// to draw.
m_invalRegion.op(firstDirtyTileX, firstDirtyTileY, lastDirtyTileX, lastDirtyTileY, SkRegion::kUnion_Op);
m_latestPictureInval = pictureCount;
}
-void TiledPage::prepareRow(bool goingLeft, int tilesInRow, int firstTileX, int y, TileSet* set)
+void TiledPage::prepareRow(bool goingLeft, int tilesInRow, int firstTileX, int y, const SkIRect& tileBounds)
{
if (y < 0)
return;
- if (!set)
- return;
for (int i = 0; i < tilesInRow; i++) {
int x = firstTileX;
// ensure there is a texture associated with the tile and then check to
// see if the texture is dirty and in need of repainting
currentTile->reserveTexture();
- if (currentTile->isDirty())
- set->add(currentTile);
+ updateTileUsedLevel(tileBounds, *currentTile);
+ if (currentTile->isDirty() && !currentTile->isRepaintPending()) {
+ PaintTileOperation *operation = new PaintTileOperation(currentTile);
+ TilesManager::instance()->scheduleOperation(operation);
+ } else if (currentTile->isDirty()) {
+ XLOG("Tile %dx%d is dirty, but awaiting repaint", currentTile->x(), currentTile->y());
+ }
}
}
}
+void TiledPage::updateTileUsedLevel(const SkIRect& tileBounds, BaseTile& tile)
+{
+ const int lastTileX = tileBounds.fRight - 1;
+ const int lastTileY = tileBounds.fBottom - 1;
+
+ // set the used level of the tile (e.g. distance from the viewport)
+ int dx = 0;
+ int dy = 0;
+
+ if (tileBounds.fLeft > tile.x())
+ dx = tileBounds.fLeft - tile.x();
+ else if (lastTileX < tile.x())
+ dx = tile.x() - lastTileX;
+
+ if (tileBounds.fTop > tile.y())
+ dy = tileBounds.fTop - tile.y();
+ else if (lastTileY < tile.y())
+ dy = tile.y() - lastTileY;
+
+ int d = std::max(dx, dy);
+
+ tile.setUsedLevel(d);
+}
+
void TiledPage::updateTileState(const SkIRect& tileBounds)
{
if (!m_glWebViewState || tileBounds.isEmpty()) {
return;
}
- const int nbTilesWidth = tileBounds.width() - 1;
- const int nbTilesHeight = tileBounds.height() - 1;
-
- const int lastTileX = tileBounds.fRight - 1;
- const int lastTileY = tileBounds.fBottom - 1;
-
for (int x = 0; x < m_baseTileSize; x++) {
BaseTile& tile = m_baseTiles[x];
- // if the tile is in the dirty region then we must invalidate it
- if (m_invalRegion.contains(tile.x(), tile.y()))
- tile.markAsDirty(m_latestPictureInval, m_invalTilesRegion);
-
// if the tile no longer has a texture then proceed to the next tile
if (tile.isAvailable())
continue;
- // set the used level of the tile (e.g. distance from the viewport)
- int dx = 0;
- int dy = 0;
-
- if (tileBounds.fLeft > tile.x())
- dx = tileBounds.fLeft - tile.x();
- else if (lastTileX < tile.x())
- dx = tile.x() - lastTileX;
-
- if (tileBounds.fTop > tile.y())
- dy = tileBounds.fTop - tile.y();
- else if (lastTileY < tile.y())
- dy = tile.y() - lastTileY;
-
- int d = std::max(dx, dy);
+ // if the tile is in the dirty region then we must invalidate it
+ if (m_invalRegion.contains(tile.x(), tile.y()))
+ tile.markAsDirty(m_latestPictureInval, m_invalTilesRegion);
- tile.setUsedLevel(d);
+ updateTileUsedLevel(tileBounds, tile);
}
// clear the invalidated region as all tiles within that region have now
m_invalTilesRegion.setEmpty();
}
-void TiledPage::prepare(bool goingDown, bool goingLeft, const SkIRect& tileBounds,
- bool scheduleFirst)
+void TiledPage::prepare(bool goingDown, bool goingLeft, const SkIRect& tileBounds)
{
if (!m_glWebViewState)
return;
// update the tiles distance from the viewport
updateTileState(tileBounds);
m_prepare = true;
+ m_scrollingDown = goingDown;
int firstTileX = tileBounds.fLeft;
int firstTileY = tileBounds.fTop;
const int baseContentHeight = m_glWebViewState->baseContentHeight();
const int baseContentWidth = m_glWebViewState->baseContentWidth();
- TileSet* set = new TileSet(this, nbTilesHeight, nbTilesWidth);
-
- if (!scheduleFirst) {
- // Expand number of tiles to allow tiles outside of viewport to be prepared for
- // smoother scrolling.
- int nTilesToPrepare = nbTilesWidth * nbTilesHeight;
- int nMaxTilesPerPage = m_baseTileSize / 2;
- int expandX = TilesManager::instance()->expandedTileBoundsX();
- int expandY = TilesManager::instance()->expandedTileBoundsY();
- if (nTilesToPrepare + (nbTilesHeight * expandX * 2) <= nMaxTilesPerPage) {
- firstTileX -= expandX;
- lastTileX += expandX;
- nbTilesWidth += expandX * 2;
- }
- if (nTilesToPrepare + (nbTilesWidth * expandY * 2) <= nMaxTilesPerPage) {
- firstTileY -= expandY;
- lastTileY += expandY;
- nbTilesHeight += expandY * 2;
- }
+ // Expand number of tiles to allow tiles outside of viewport to be prepared for
+ // smoother scrolling.
+ int nTilesToPrepare = nbTilesWidth * nbTilesHeight;
+ int nMaxTilesPerPage = m_baseTileSize / 2;
+ int expandX = TilesManager::instance()->expandedTileBoundsX();
+ int expandY = TilesManager::instance()->expandedTileBoundsY();
+ if (nTilesToPrepare + (nbTilesHeight * expandX * 2) <= nMaxTilesPerPage) {
+ firstTileX -= expandX;
+ lastTileX += expandX;
+ nbTilesWidth += expandX * 2;
}
-
- // We chose to prepare tiles depending on the scroll direction. Tiles are
- // appended to the list and the texture uploader goes through the list front
- // to back. So we append tiles in reverse order because the last additions
- // to the are processed first.
- if (goingDown) {
- for (int i = 0; i < nbTilesHeight; i++)
- prepareRow(goingLeft, nbTilesWidth, firstTileX, lastTileY - i, set);
- } else {
- for (int i = 0; i < nbTilesHeight; i++)
- prepareRow(goingLeft, nbTilesWidth, firstTileX, firstTileY + i, set);
+ if (nTilesToPrepare + (nbTilesWidth * expandY * 2) <= nMaxTilesPerPage) {
+ firstTileY -= expandY;
+ lastTileY += expandY;
+ nbTilesHeight += expandY * 2;
}
+ m_expandedTileBounds.fLeft = firstTileX;
+ m_expandedTileBounds.fTop = firstTileY;
+ m_expandedTileBounds.fRight = lastTileX;
+ m_expandedTileBounds.fBottom = lastTileY;
- // The paint operation will take ownership of the tileSet here, so no delete
- // is necessary.
- PaintTileSetOperation* operation = new PaintTileSetOperation(set);
- TilesManager::instance()->scheduleOperation(operation, scheduleFirst);
+ for (int i = 0; i < nbTilesHeight; i++)
+ prepareRow(goingLeft, nbTilesWidth, firstTileX, firstTileY + i, tileBounds);
}
bool TiledPage::ready(const SkIRect& tileBounds, float scale)
actualTileBounds.fLeft -= TilesManager::instance()->expandedTileBoundsX();
actualTileBounds.fRight += TilesManager::instance()->expandedTileBoundsX();
- XLOG("WE DRAW %x (%.2f) with transparency %.2f", this, scale(), transparency);
for (int j = 0; j < m_baseTileSize; j++) {
BaseTile& tile = m_baseTiles[j];
if (actualTileBounds.contains(tile.x(), tile.y())) {
tile.draw(transparency, rect, m_scale);
}
}
-
-#ifdef DEBUG
- XLOG("FINISHED WE DRAW %x (%.2f) with transparency %.2f", this, scale(), transparency);
- TilesManager::instance()->printTextures();
-#endif // DEBUG
}
unsigned int TiledPage::paintBaseLayerContent(SkCanvas* canvas)
#include "BaseTile.h"
#include "SkCanvas.h"
#include "SkRegion.h"
-#include "TileSet.h"
namespace WebCore {
TiledPage* sibling();
// prepare the page for display on the screen
- void prepare(bool goingDown, bool goingLeft, const SkIRect& tileBounds,
- bool scheduleFirst = false);
+ void prepare(bool goingDown, bool goingLeft, const SkIRect& tileBounds);
// check to see if the page is ready for display
bool ready(const SkIRect& tileBounds, float scale);
// draw the page on the screen
void invalidateRect(const IntRect& invalRect, const unsigned int pictureCount);
void setUsable(bool usable);
void updateBaseTileSize();
+ bool scrollingDown() { return m_scrollingDown; }
+ SkIRect* expandedTileBounds() { return &m_expandedTileBounds; }
private:
void updateTileState(const SkIRect& tileBounds);
- void prepareRow(bool goingLeft, int tilesInRow, int firstTileX, int y, TileSet* set);
+ void prepareRow(bool goingLeft, int tilesInRow, int firstTileX, int y, const SkIRect& tileBounds);
+ void updateTileUsedLevel(const SkIRect& tileBounds, BaseTile& tile);
BaseTile* getBaseTile(int x, int y) const;
SkRegion m_invalTilesRegion;
unsigned int m_latestPictureInval;
bool m_prepare;
+ bool m_scrollingDown;
+ SkIRect m_expandedTileBounds;
};
} // namespace WebCore
namespace WebCore {
-class TileSet;
-
class TilesManager {
public:
static TilesManager* instance();
m_pixmapsGenerationThread->removeOperationsForPage(page);
}
+ void removePaintOperationsForPage(TiledPage* page, bool waitForCompletion)
+ {
+ m_pixmapsGenerationThread->removePaintOperationsForPage(page, waitForCompletion);
+ }
+
void removeOperationsForBaseLayer(BaseLayerAndroid* layer)
{
m_pixmapsGenerationThread->removeOperationsForBaseLayer(layer);
m_pixmapsGenerationThread->removeOperationsForTexture(texture);
}
- void scheduleOperation(QueuedOperation* operation, bool scheduleFirst = false)
+ void scheduleOperation(QueuedOperation* operation)
{
- m_pixmapsGenerationThread->scheduleOperation(operation, scheduleFirst);
+ m_pixmapsGenerationThread->scheduleOperation(operation);
}
ShaderProgram* shader() { return &m_shader; }
DBG_SET_LOG("!m_mainFrame->document()");
return;
}
+ // If there is a pending style recalculation, just return.
+ if (m_mainFrame->document()->isPendingStyleRecalc()) {
+ LOGW("recordPictureSet: pending style recalc, ignoring.");
+ return;
+ }
if (m_addInval.isEmpty()) {
DBG_SET_LOG("m_addInval.isEmpty()");
return;
void WebViewCore::notifyProgressFinished()
{
- DBG_NAV_LOG("call updateFrameCache");
m_check_domtree_version = true;
- updateFrameCache();
sendNotifyProgressFinished();
}
DBG_NAV_LOG("!m_frameCacheOutOfDate");
return;
}
+
+ // If there is a pending style recalculation, do not update the frame cache.
+ // Until the recalculation is complete, there may be internal objects that
+ // are in an inconsistent state (such as font pointers).
+ // In any event, there's not much point to updating the cache while a style
+ // recalculation is pending, since it will simply have to be updated again
+ // once the recalculation is complete.
+ // TODO: Do we need to reschedule an update for after the style is recalculated?
+ if (m_mainFrame && m_mainFrame->document() && m_mainFrame->document()->isPendingStyleRecalc()) {
+ LOGW("updateFrameCache: pending style recalc, ignoring.");
+ return;
+ }
#ifdef ANDROID_INSTRUMENT
TimeCounterAuto counter(TimeCounter::WebViewCoreBuildNavTimeCounter);
#endif
return;
SkRect visible;
calcOurContentVisibleRect(&visible);
-#if USE(ACCELERATED_COMPOSITING)
- LayerAndroid* root = compositeRoot();
- if (root) {
- root->updateFixedLayersPositions(visible);
- root->updatePositions();
- visible = root->subtractLayers(visible);
- }
-#endif
int dx = 0;
int left = rect.x();
int right = rect.right();