From f6a40880028cf495025747229c627f42e25acc66 Mon Sep 17 00:00:00 2001 From: Chris Craik Date: Tue, 27 Sep 2011 10:17:32 -0700 Subject: Use state machine to track tile state bug:5369978 Manage invalidations, painting, transferring, and swapping with a state machine. notes: * readyFor shouldn't be needed if all of the events that would cause a readyfor fail mark the tile dirty (such as stealing, scale change) * changing the scale of a page should discard all textures * m_dirty should be more gracefully worked into the state machine * a tile may drop dirtiness notifications if it's already painting, since upon completion m_dirty will be cleared - (issues for layers only) Change-Id: I5909fb5d208da2fb276e223c56bf143741a9a24c --- .../WebCore/platform/graphics/android/BaseTile.cpp | 137 ++++++++++++++++----- .../WebCore/platform/graphics/android/BaseTile.h | 58 +++++++-- .../platform/graphics/android/BaseTileTexture.cpp | 22 +++- .../platform/graphics/android/TiledPage.cpp | 28 ++--- .../WebCore/platform/graphics/android/TiledPage.h | 2 + .../platform/graphics/android/TilesManager.cpp | 5 +- .../platform/graphics/android/TransferQueue.cpp | 1 + 7 files changed, 187 insertions(+), 66 deletions(-) (limited to 'Source') diff --git a/Source/WebCore/platform/graphics/android/BaseTile.cpp b/Source/WebCore/platform/graphics/android/BaseTile.cpp index a4ce788..4a15f9f 100644 --- a/Source/WebCore/platform/graphics/android/BaseTile.cpp +++ b/Source/WebCore/platform/graphics/android/BaseTile.cpp @@ -70,8 +70,8 @@ BaseTile::BaseTile(bool isLayerTile) , m_lastDirtyPicture(0) , m_isTexturePainted(false) , m_isLayerTile(isLayerTile) - , m_swapDrawCount(0) , m_drawCount(0) + , m_state(Unpainted) { #ifdef DEBUG_COUNT ClassTracker::instance()->increment("BaseTile"); @@ -113,13 +113,15 @@ BaseTile::~BaseTile() void BaseTile::setContents(TilePainter* painter, int x, int y, float scale) { - android::AutoMutex lock(m_atomicSync); if ((m_painter != painter) || (m_x != x) || (m_y != y) - || (m_scale != scale)) - fullInval(); + || (m_scale != scale)) { + // neither texture is relevant + discardTextures(); + } + android::AutoMutex lock(m_atomicSync); m_painter = painter; m_x = x; m_y = y; @@ -133,14 +135,15 @@ void BaseTile::reserveTexture() android::AutoMutex lock(m_atomicSync); if (texture && m_backTexture != texture) { - m_swapDrawCount = 0; // no longer ready to swap + m_state = Unpainted; m_backTexture = texture; } - // a texture reservation will only happen if we're dirty, or ready to - // swap. if it's the former, ensure it's marked dirty. - if (!m_swapDrawCount) + if (m_state == UpToDate) { + XLOG("moving tile %p to unpainted, since it reserved while up to date", this); m_dirty = true; + m_state = Unpainted; + } } bool BaseTile::removeTexture(BaseTileTexture* texture) @@ -149,10 +152,18 @@ bool BaseTile::removeTexture(BaseTileTexture* texture) this, m_backTexture, m_frontTexture, m_page); // We update atomically, so paintBitmap() can see the correct value android::AutoMutex lock(m_atomicSync); - if (m_frontTexture == texture) + if (m_frontTexture == texture) { + if (m_state == UpToDate) { + XLOG("front texture removed, state was UpToDate, now becoming unpainted, bt is %p", m_backTexture); + m_state = Unpainted; + } + m_frontTexture = 0; - if (m_backTexture == texture) + } + if (m_backTexture == texture) { + m_state = Unpainted; m_backTexture = 0; + } // mark dirty regardless of which texture was taken - the back texture may // have been ready to swap @@ -161,15 +172,6 @@ bool BaseTile::removeTexture(BaseTileTexture* texture) return true; } -void BaseTile::fullInval() -{ - for (int i = 0; i < m_maxBufferNumber; i++) { - m_dirtyArea[i].setEmpty(); - m_fullRepaint[i] = true; - } - m_dirty = true; -} - void BaseTile::markAsDirty(int unsigned pictureCount, const SkRegion& dirtyArea) { @@ -180,6 +182,20 @@ void BaseTile::markAsDirty(int unsigned pictureCount, for (int i = 0; i < m_maxBufferNumber; i++) m_dirtyArea[i].op(dirtyArea, SkRegion::kUnion_Op); m_dirty = true; + if (m_state == UpToDate) { + // We only mark a tile as unpainted in 'markAsDirty' if its status is + // UpToDate: marking dirty means we need to repaint, but don't stop the + // current paint + m_state = Unpainted; + } else if (m_state != Unpainted) { + // layer tiles and prefetch page tiles are potentially marked dirty + // while in the process of painting, due to not using an update lock + + // TODO: fix it so that they can paint while deferring the markAsDirty + // call (or block updates) + XLOG("Warning: tried to mark tile %p at %d, %d islayertile %d as dirty, state %d, page %p", + this, m_x, m_y, isLayerTile(), m_state, m_page); + } } bool BaseTile::isDirty() @@ -232,8 +248,9 @@ void BaseTile::draw(float transparency, SkRect& rect, float scale) else TilesManager::instance()->shader()->drawQuad(rect, m_frontTexture->m_ownTextureId, transparency); - } else - m_dirty = true; + } else { + XLOG("tile %p at %d, %d not readyfor (at draw),", this, m_x, m_y); + } m_frontTexture->consumerRelease(); } @@ -242,7 +259,7 @@ bool BaseTile::isTileReady() { // Return true if the tile's most recently drawn texture is up to date android::AutoMutex lock(m_atomicSync); - BaseTileTexture * texture = m_swapDrawCount ? m_backTexture : m_frontTexture; + BaseTileTexture * texture = (m_state == ReadyToSwap) ? m_backTexture : m_frontTexture; if (!texture) return false; @@ -253,6 +270,9 @@ bool BaseTile::isTileReady() if (m_dirty) return false; + if (m_state != ReadyToSwap && m_state != UpToDate) + return false; + texture->consumerLock(); bool ready = texture->readyFor(this); texture->consumerRelease(); @@ -260,7 +280,8 @@ bool BaseTile::isTileReady() if (ready) return true; - m_dirty = true; + XLOG("tile %p at %d, %d not readyfor (at isTileReady)", this, m_x, m_y); + return false; } @@ -303,6 +324,11 @@ void BaseTile::paintBitmap() m_atomicSync.unlock(); return; } + if (m_state != Unpainted) { + XLOG("Warning: started painting tile %p, but was at state %d, ft %p bt %p", + this, m_state, m_frontTexture, m_backTexture); + } + m_state = PaintingStarted; texture->producerAcquireContext(); TextureInfo* textureInfo = texture->producerLock(); @@ -439,11 +465,10 @@ void BaseTile::paintBitmap() XLOG("painted tile %p (%d, %d), texture %p, dirty=%d", this, x, y, texture, m_dirty); - if (!m_dirty) { - // swap textures, but WAIT until the next draw call (since we need - // to let GLWebViewState blit them at the beginning of drawGL) - m_swapDrawCount = TilesManager::instance()->getDrawGLCount() + 1; - } + validatePaint(); + } else { + XLOG("tile %p no longer owns texture %p, m_state %d. ft %p bt %p", + this, texture, m_state, m_frontTexture, m_backTexture); } m_atomicSync.unlock(); @@ -459,27 +484,73 @@ void BaseTile::discardTextures() { m_backTexture->release(this); m_backTexture = 0; } + for (int i = 0; i < m_maxBufferNumber; i++) { + m_dirtyArea[i].setEmpty(); + m_fullRepaint[i] = true; + } m_dirty = true; + m_state = Unpainted; } bool BaseTile::swapTexturesIfNeeded() { android::AutoMutex lock(m_atomicSync); - if (m_swapDrawCount && TilesManager::instance()->getDrawGLCount() >= m_swapDrawCount) { + if (m_state == ReadyToSwap) { // discard old texture and swap the new one in its place if (m_frontTexture) m_frontTexture->release(this); - XLOG("%p's frontTexture was %p, now becoming %p", this, m_frontTexture, m_backTexture); m_frontTexture = m_backTexture; m_backTexture = 0; - m_swapDrawCount = 0; - XLOG("display texture for %d, %d front is now %p, texture is %p", - m_x, m_y, m_frontTexture, m_backTexture); + m_state = UpToDate; + XLOG("display texture for %p at %d, %d front is now %p, back is %p", + this, m_x, m_y, m_frontTexture, m_backTexture); + return true; } return false; } +void BaseTile::backTextureTransfer() { + android::AutoMutex lock(m_atomicSync); + if (m_state == PaintingStarted) + m_state = TransferredUnvalidated; + else if (m_state == ValidatedUntransferred) + m_state = ReadyToSwap; + else { + // shouldn't have transferred a tile in any other state, log + XLOG("Note: transferred tile %p at %d %d, state wasn't paintingstarted or validated: %d", + this, m_x, m_y, m_state); + } +} + +void BaseTile::validatePaint() { + // ONLY CALL while m_atomicSync is locked (at the end of paintBitmap()) + + if (!m_dirty) { + // since after the paint, the tile isn't dirty, 'validate' it - this + // may happed before or after the transfer queue operation. Only + // when both have happened, mark as 'ReadyToSwap' + if (m_state == PaintingStarted) + m_state = ValidatedUntransferred; + else if (m_state == TransferredUnvalidated) + m_state = ReadyToSwap; + else { + // shouldn't have just finished painting in any other state, log + XLOG("Note: validated tile %p at %d %d, state wasn't paintingstarted or transferred %d", + this, m_x, m_y, m_state); + } + + if (m_deferredDirty) { + XLOG("Note: deferred dirty flag set, possibly a missed paint on tile %p", this); + m_deferredDirty = false; + } + } else { + XLOG("Note: paint was unsuccessful."); + m_state = Unpainted; + } + +} + } // namespace WebCore #endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/android/BaseTile.h b/Source/WebCore/platform/graphics/android/BaseTile.h index 734a3c8..6d0fa8f 100644 --- a/Source/WebCore/platform/graphics/android/BaseTile.h +++ b/Source/WebCore/platform/graphics/android/BaseTile.h @@ -61,6 +61,34 @@ class GLWebViewState; */ class BaseTile : public TextureOwner { public: + + // eventually, m_dirty might be rolled into the state machine, but note + // that a tile that's continually marked dirty from animation should still + // progress through the state machine and be drawn periodically (esp. for + // layers) + + // /-> TransferredUnvalidated (TQ interrupts paint) -\ (TQ & paint done) + // Unpainted -> PaintingStarted -- -> ReadyToSwap -> UpToDate + // ^ \-> ValidatedUntransferred (paint finish before TQ) -/ + // | + // \--... (From any state when marked dirty. should usually come from UpToDate if the updates are locked) + // + + enum TextureState{ + // back texture is completely unpainted + Unpainted = 0, + // has started painting, but haven't been transferred or validated + PaintingStarted = 1, + // back texture painted, transferred before validating in PaintBitmap() + TransferredUnvalidated = 2, + // back texture painted, validated before transferring in TransferQueue + ValidatedUntransferred = 3, + // back texture has been blitted, will be swapped when next available + ReadyToSwap = 4, + // has been swapped, is ready to draw, all is well + UpToDate = 5, + }; + BaseTile(bool isLayerTile = false); ~BaseTile(); @@ -88,15 +116,18 @@ public: bool isRepaintPending(); void setRepaintPending(bool pending); float scale() const { return m_scale; } - void fullInval(); + TextureState textureState() const { return m_state; } int x() const { return m_x; } int y() const { return m_y; } BaseTileTexture* frontTexture() { return m_frontTexture; } BaseTileTexture* backTexture() { return m_backTexture; } + + // only used for prioritization - the higher, the more relevant the tile is + unsigned long long drawCount() { return m_drawCount; } void discardTextures(); bool swapTexturesIfNeeded(); - unsigned long long drawCount() { return m_drawCount; } + void backTextureTransfer(); void setGLWebViewState(GLWebViewState* state) { m_glWebViewState = state; } @@ -107,6 +138,8 @@ public: TilePainter* painter() { return m_painter; } private: + void validatePaint(); + GLWebViewState* m_glWebViewState; TilePainter* m_painter; @@ -124,6 +157,10 @@ private: // used to signal that the that the tile is out-of-date and needs to be // redrawn in the backTexture bool m_dirty; + + // currently only for debugging, to be used for tracking down dropped repaints + bool m_deferredDirty; + // used to signal that a repaint is pending bool m_repaintPending; // stores the id of the latest picture from webkit that caused this tile to @@ -149,18 +186,19 @@ private: bool m_isLayerTile; - // this is set when the back texture is finished painting and should be - // swapped to the front. it is set with the NEXT drawGL call (see - // TilesManager::m_drawGLCount) so that the textures may be blitted at the - // beginning of GLWebViewState::drawGL before they are swapped - - // 4 steps for texture: paint -> blit -> swap -> draw - unsigned long long m_swapDrawCount; - // the most recent GL draw before this tile was prepared. used for // prioritization and caching. tiles with old drawcounts and textures they // own are used for new tiles and rendering unsigned long long m_drawCount; + + // Tracks the state of painting for the tile. High level overview: + // 1) Unpainted - until paint starts (and if marked dirty, in most cases) + // 2) PaintingStarted - until paint completes + // 3) TransferredUnvalidated - if transferred first + // or ValidatedUntransferred - if validated first + // 4) ReadyToSwap - if painted and transferred, but not swapped + // 5) UpToDate - until marked dirty again + TextureState m_state; }; } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/android/BaseTileTexture.cpp b/Source/WebCore/platform/graphics/android/BaseTileTexture.cpp index d60d695..54c96c8 100644 --- a/Source/WebCore/platform/graphics/android/BaseTileTexture.cpp +++ b/Source/WebCore/platform/graphics/android/BaseTileTexture.cpp @@ -32,11 +32,14 @@ #include "GLUtils.h" #include "TilesManager.h" -#ifdef DEBUG - #include #include +#undef XLOGC +#define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "BaseTileTexture", __VA_ARGS__) + +#ifdef DEBUG + #undef XLOG #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "BaseTileTexture", __VA_ARGS__) @@ -89,7 +92,12 @@ void BaseTileTexture::discardTexture() { if (m_ownTextureId) GLUtils::deleteTexture(&m_ownTextureId); - release(m_owner); + + if (m_owner) { + // clear both Tile->Texture and Texture->Tile links + m_owner->removeTexture(this); + release(m_owner); + } } void BaseTileTexture::destroyTextures(SharedTexture** textures) @@ -227,9 +235,6 @@ bool BaseTileTexture::release(TextureOwner* owner) if (m_owner != owner) return false; - // force readyFor to return false next call (even if texture reaquired by same tile) - m_ownTextureTileInfo.m_x = -1; - m_ownTextureTileInfo.m_y = -1; if (!m_busy) { m_owner = 0; } else { @@ -271,6 +276,11 @@ void BaseTileTexture::setOwnTextureTileInfoFromQueue(const TextureTileInfo* info m_ownTextureTileInfo.m_painter = info->m_painter; m_ownTextureTileInfo.m_picture = info->m_picture; m_ownTextureTileInfo.m_inverted = TilesManager::instance()->invertedScreen(); + if (m_owner) { + BaseTile* owner = static_cast(m_owner); + owner->backTextureTransfer(); + } + } bool BaseTileTexture::readyFor(BaseTile* baseTile) diff --git a/Source/WebCore/platform/graphics/android/TiledPage.cpp b/Source/WebCore/platform/graphics/android/TiledPage.cpp index 0c7a2a8..34f0d4e 100644 --- a/Source/WebCore/platform/graphics/android/TiledPage.cpp +++ b/Source/WebCore/platform/graphics/android/TiledPage.cpp @@ -300,6 +300,7 @@ bool TiledPage::swapBuffersIfReady(const SkIRect& tileBounds, float scale, SwapM return false; int swaps = 0; + bool fullSwap = true; if (swap == SwapWholePage) { for (int x = tileBounds.fLeft; x < tileBounds.fRight; x++) { for (int y = tileBounds.fTop; y < tileBounds.fBottom; y++) { @@ -308,31 +309,26 @@ bool TiledPage::swapBuffersIfReady(const SkIRect& tileBounds, float scale, SwapM return false; } } - for (int x = tileBounds.fLeft; x < tileBounds.fRight; x++) { - for (int y = tileBounds.fTop; y < tileBounds.fBottom; y++) { - BaseTile* t = getBaseTile(x, y); - if (t->swapTexturesIfNeeded()) - swaps++; - } - } - XLOG("%p whole page swapped %d textures, returning true", this, swaps); - return true; } else { // SwapWhateveryIsReady - bool fullSwap = true; for (int x = tileBounds.fLeft; x < tileBounds.fRight; x++) { for (int y = tileBounds.fTop; y < tileBounds.fBottom; y++) { BaseTile* t = getBaseTile(x, y); if (!t || !t->isTileReady()) fullSwap = false; - else { - if (t->swapTexturesIfNeeded()) - swaps++; - } } } - XLOG("%p greedy swap swapped %d tiles, returning %d", this, swaps, fullSwap); - return fullSwap; } + + // swap every tile on page (even if off screen) + for (int j = 0; j < m_baseTileSize; j++) { + BaseTile& tile = m_baseTiles[j]; + if (tile.swapTexturesIfNeeded()) + swaps++; + } + + XLOG("%p %s swapped %d textures, returning true", + this, (swap == SwapWholePage) ? "whole page" : "greedy swap", swaps); + return fullSwap; } diff --git a/Source/WebCore/platform/graphics/android/TiledPage.h b/Source/WebCore/platform/graphics/android/TiledPage.h index c903abc..780a604 100644 --- a/Source/WebCore/platform/graphics/android/TiledPage.h +++ b/Source/WebCore/platform/graphics/android/TiledPage.h @@ -91,6 +91,8 @@ public: GLWebViewState* glWebViewState() { return m_glWebViewState; } float scale() const { return m_scale; } + + //TODO: clear all textures if this is called with a new value void setScale(float scale) { m_scale = scale; m_invScale = 1 / scale; } void invalidateRect(const IntRect& invalRect, const unsigned int pictureCount); diff --git a/Source/WebCore/platform/graphics/android/TilesManager.cpp b/Source/WebCore/platform/graphics/android/TilesManager.cpp index 8724fbc..d97eb8e 100644 --- a/Source/WebCore/platform/graphics/android/TilesManager.cpp +++ b/Source/WebCore/platform/graphics/android/TilesManager.cpp @@ -319,9 +319,12 @@ BaseTileTexture* TilesManager::getAvailableTexture(BaseTile* owner) BaseTile* previousOwner = static_cast(farthestTexture->owner()); if (farthestTexture->acquire(owner)) { if (previousOwner) { - XLOG("%s texture %p stolen from tile %d, %d, drawCount was %llu (current is %llu)", + previousOwner->removeTexture(farthestTexture); + + XLOG("%s texture %p stolen from tile %d, %d for %d, %d, drawCount was %llu (now %llu)", owner->isLayerTile() ? "LAYER" : "BASE", farthestTexture, previousOwner->x(), previousOwner->y(), + owner->x(), owner->y(), oldestDrawCount, getDrawGLCount()); } diff --git a/Source/WebCore/platform/graphics/android/TransferQueue.cpp b/Source/WebCore/platform/graphics/android/TransferQueue.cpp index fa7f5b6..a6b2e26 100644 --- a/Source/WebCore/platform/graphics/android/TransferQueue.cpp +++ b/Source/WebCore/platform/graphics/android/TransferQueue.cpp @@ -60,6 +60,7 @@ TransferQueue::TransferQueue() , m_fboID(0) , m_sharedSurfaceTextureId(0) , m_hasGLContext(true) + , m_interruptedByRemovingOp(false) , m_currentDisplay(EGL_NO_DISPLAY) , m_currentUploadType(DEFAULT_UPLOAD_TYPE) { -- cgit v1.1