platform/graphics/android/TiledPage.cpp \
platform/graphics/android/TiledTexture.cpp \
platform/graphics/android/TransferQueue.cpp \
- platform/graphics/android/UpdateManager.cpp \
+ platform/graphics/android/TreeManager.cpp \
platform/graphics/android/VerticalTextMap.cpp \
platform/graphics/android/VideoLayerAndroid.cpp \
platform/graphics/android/VideoLayerManager.cpp \
BaseLayerAndroid::BaseLayerAndroid()
#if USE(ACCELERATED_COMPOSITING)
- : m_glWebViewState(0)
- , m_color(Color::white)
+ : m_color(Color::white)
, m_scrollState(NotScrolling)
#endif
{
/ TilesManager::instance()->tileWidth();
float invTileHeight = (prefetchScale)
/ TilesManager::instance()->tileHeight();
- bool goingDown = m_glWebViewState->goingDown();
- bool goingLeft = m_glWebViewState->goingLeft();
+ bool goingDown = m_state->goingDown();
+ bool goingLeft = m_state->goingLeft();
XLOG("fetch rect %f %f %f %f, scale %f",
prefetchTiledPage->prepare(goingDown, goingLeft, bounds,
TiledPage::ExpandedBounds);
prefetchTiledPage->swapBuffersIfReady(bounds,
- prefetchScale,
- TiledPage::SwapWhateverIsReady);
+ prefetchScale);
if (draw)
- prefetchTiledPage->draw(PREFETCH_OPACITY, bounds);
+ prefetchTiledPage->prepareForDrawGL(PREFETCH_OPACITY, bounds);
}
-bool BaseLayerAndroid::drawBasePictureInGL(SkRect& viewport, float scale,
- double currentTime, bool* buffersSwappedPtr)
+bool BaseLayerAndroid::isReady()
{
- ZoomManager* zoomManager = m_glWebViewState->zoomManager();
+ ZoomManager* zoomManager = m_state->zoomManager();
+ if (ZoomManager::kNoScaleRequest != zoomManager->scaleRequestState()) {
+ XLOG("base layer not ready, still zooming");
+ return false; // still zooming
+ }
+
+ if (!m_state->frontPage()->isReady(m_state->preZoomBounds())) {
+ XLOG("base layer not ready, front page not done painting");
+ return false;
+ }
+
+ LayerAndroid* compositedRoot = static_cast<LayerAndroid*>(getChild(0));
+ if (compositedRoot) {
+ XLOG("base layer is ready, how about children?");
+ return compositedRoot->isReady();
+ }
+
+ return true;
+}
+
+void BaseLayerAndroid::swapTiles()
+{
+ if (countChildren())
+ getChild(0)->swapTiles(); // TODO: move to parent impl
+
+ m_state->frontPage()->swapBuffersIfReady(m_state->preZoomBounds(),
+ m_state->zoomManager()->currentScale());
+
+ m_state->backPage()->swapBuffersIfReady(m_state->preZoomBounds(),
+ m_state->zoomManager()->currentScale());
+}
+
+void BaseLayerAndroid::setIsDrawing(bool isDrawing)
+{
+ if (countChildren())
+ getChild(0)->setIsDrawing(isDrawing); // TODO: move to parent impl
+}
+
+void BaseLayerAndroid::setIsPainting(Layer* drawingTree)
+{
+ XLOG("BLA %p painting, dirty %d", this, isDirty());
+ if (drawingTree)
+ drawingTree = drawingTree->getChild(0);
+
+ if (countChildren())
+ getChild(0)->setIsPainting(drawingTree); // TODO: move to parent impl
+
+ m_state->invalRegion(m_dirtyRegion);
+ m_dirtyRegion.setEmpty();
+}
+
+void BaseLayerAndroid::mergeInvalsInto(Layer* replacementTree)
+{
+ XLOG("merging invals (empty=%d) from BLA %p to %p", m_dirtyRegion.isEmpty(), this, replacementTree);
+ if (countChildren() && replacementTree->countChildren())
+ getChild(0)->mergeInvalsInto(replacementTree->getChild(0));
- bool goingDown = m_glWebViewState->goingDown();
- bool goingLeft = m_glWebViewState->goingLeft();
+ replacementTree->markAsDirty(m_dirtyRegion);
+}
+
+bool BaseLayerAndroid::prepareBasePictureInGL(SkRect& viewport, float scale,
+ double currentTime)
+{
+ ZoomManager* zoomManager = m_state->zoomManager();
+
+ bool goingDown = m_state->goingDown();
+ bool goingLeft = m_state->goingLeft();
- const SkIRect& viewportTileBounds = m_glWebViewState->viewportTileBounds();
+ const SkIRect& viewportTileBounds = m_state->viewportTileBounds();
XLOG("drawBasePicture, TX: %d, TY: %d scale %.2f", viewportTileBounds.fLeft,
viewportTileBounds.fTop, scale);
bool prepareNextTiledPage = zoomManager->needPrepareNextTiledPage();
// Display the current page
- TiledPage* tiledPage = m_glWebViewState->frontPage();
- TiledPage* nextTiledPage = m_glWebViewState->backPage();
+ TiledPage* tiledPage = m_state->frontPage();
+ TiledPage* nextTiledPage = m_state->backPage();
tiledPage->setScale(zoomManager->currentScale());
// Let's prepare the page if needed so that it will start painting
if (prepareNextTiledPage) {
nextTiledPage->setScale(scale);
- m_glWebViewState->setFutureViewport(viewportTileBounds);
- m_glWebViewState->lockBaseLayerUpdate();
+ m_state->setFutureViewport(viewportTileBounds);
// ignore dirtiness return value since while zooming we repaint regardless
nextTiledPage->updateTileDirtiness(viewportTileBounds);
// If we fired a request, let's check if it's ready to use
if (zoomManager->didFireRequest()) {
if (nextTiledPage->swapBuffersIfReady(viewportTileBounds,
- zoomManager->futureScale(),
- TiledPage::SwapWholePage))
+ zoomManager->futureScale()))
zoomManager->setReceivedRequest(); // transition to received request state
}
float nextTiledPageTransparency = 1;
zoomManager->processTransition(currentTime, scale, &doZoomPageSwap,
&nextTiledPageTransparency, &transparency);
- nextTiledPage->draw(nextTiledPageTransparency, viewportTileBounds);
+ nextTiledPage->prepareForDrawGL(nextTiledPageTransparency, viewportTileBounds);
}
- const SkIRect& preZoomBounds = m_glWebViewState->preZoomBounds();
+ const SkIRect& preZoomBounds = m_state->preZoomBounds();
- // update scrolling state machine by querying glwebviewstate - note that the
- // NotScrolling state is only set below
- if (m_glWebViewState->isScrolling())
- m_scrollState = Scrolling;
- else if (m_scrollState == Scrolling)
- m_scrollState = ScrollingFinishPaint;
-
- bool scrolling = m_scrollState != NotScrolling;
bool zooming = ZoomManager::kNoScaleRequest != zoomManager->scaleRequestState();
- // When we aren't zooming, we should TRY and swap tile buffers if they're
- // ready. When scrolling, we swap whatever's ready. Otherwise, buffer until
- // the entire page is ready and then swap.
- bool tilesFinished = false;
- if (!zooming) {
- TiledPage::SwapMethod swapMethod;
- if (scrolling)
- swapMethod = TiledPage::SwapWhateverIsReady;
- else
- swapMethod = TiledPage::SwapWholePage;
-
- tilesFinished = tiledPage->swapBuffersIfReady(preZoomBounds,
- zoomManager->currentScale(),
- swapMethod);
-
- if (buffersSwappedPtr && tilesFinished)
- *buffersSwappedPtr = true;
- if (tilesFinished) {
- if (m_scrollState == ScrollingFinishPaint) {
- m_scrollState = NotScrolling;
- scrolling = false;
- }
- }
- }
-
if (doZoomPageSwap) {
zoomManager->setCurrentScale(scale);
- m_glWebViewState->swapPages();
- if (buffersSwappedPtr)
- *buffersSwappedPtr = true;
+ m_state->swapPages();
}
-
- bool needsRedraw = scrolling || zooming || !tilesFinished;
-
- // if we don't expect to redraw, unlock the invals
- if (!needsRedraw)
- m_glWebViewState->unlockBaseLayerUpdate();
+ bool needsRedraw = zooming;
// if applied invals mark tiles dirty, need to redraw
needsRedraw |= tiledPage->updateTileDirtiness(preZoomBounds);
- if (needsRedraw) {
- // lock and paint what's needed unless we're zooming, since the new
- // tiles won't be relevant soon anyway
- m_glWebViewState->lockBaseLayerUpdate();
- if (!zooming)
- tiledPage->prepare(goingDown, goingLeft, preZoomBounds,
- TiledPage::ExpandedBounds);
- }
+ // paint what's needed unless we're zooming, since the new tiles won't
+ // be relevant soon anyway
+ if (!zooming)
+ tiledPage->prepare(goingDown, goingLeft, preZoomBounds,
+ TiledPage::ExpandedBounds);
- XLOG("scrolling %d, zooming %d, tilesFinished %d, needsRedraw %d",
- scrolling, zooming, tilesFinished, needsRedraw);
+ XLOG("scrolling %d, zooming %d, needsRedraw %d",
+ scrolling, zooming, needsRedraw);
// prefetch in the nextTiledPage if unused by zooming (even if not scrolling
// since we want the tiles to be ready before they're needed)
prefetchBasePicture(viewport, scale, nextTiledPage, drawPrefetchPage);
}
- tiledPage->draw(transparency, preZoomBounds);
+ tiledPage->prepareForDrawGL(transparency, preZoomBounds);
return needsRedraw;
}
+
+void BaseLayerAndroid::drawBasePictureInGL()
+{
+ m_state->backPage()->drawGL();
+ m_state->frontPage()->drawGL();
+}
+
#endif // USE(ACCELERATED_COMPOSITING)
-bool BaseLayerAndroid::drawGL(double currentTime, LayerAndroid* compositedRoot,
- IntRect& viewRect, SkRect& visibleRect, float scale,
- bool* buffersSwappedPtr)
+void BaseLayerAndroid::updateLayerPositions(SkRect& visibleRect)
{
- bool needsRedraw = false;
-#if USE(ACCELERATED_COMPOSITING)
+ LayerAndroid* compositedRoot = static_cast<LayerAndroid*>(getChild(0));
+ TransformationMatrix ident;
+ compositedRoot->updateFixedLayersPositions(visibleRect);
+ FloatRect clip(0, 0, content()->width(), content()->height());
+ compositedRoot->updateGLPositionsAndScale(
+ ident, clip, 1, m_state->zoomManager()->layersScale());
+
+#ifdef DEBUG
+ compositedRoot->showLayer(0);
+ XLOG("We have %d layers, %d textured",
+ compositedRoot->nbLayers(),
+ compositedRoot->nbTexturedLayers());
+#endif
+}
- needsRedraw = drawBasePictureInGL(visibleRect, scale, currentTime,
- buffersSwappedPtr);
+bool BaseLayerAndroid::prepare(double currentTime, IntRect& viewRect,
+ SkRect& visibleRect, float scale)
+{
+ XLOG("preparing BLA %p", this);
- if (!needsRedraw)
- m_glWebViewState->resetFrameworkInval();
+ // base layer is simply drawn in prepare, since there is always a base layer it doesn't matter
+ bool needsRedraw = prepareBasePictureInGL(visibleRect, scale, currentTime);
+ LayerAndroid* compositedRoot = static_cast<LayerAndroid*>(getChild(0));
if (compositedRoot) {
- SkMatrix matrix;
- matrix.setTranslate(viewRect.x(), viewRect.y());
+ updateLayerPositions(visibleRect);
-#ifdef DEBUG
- compositedRoot->showLayer(0);
- XLOG("We have %d layers, %d textured",
- compositedRoot->nbLayers(),
- compositedRoot->nbTexturedLayers());
-#endif
+ XLOG("preparing BLA %p, root %p", this, compositedRoot);
+ compositedRoot->prepare();
+ }
+
+ return needsRedraw;
+}
+
+bool BaseLayerAndroid::drawGL(IntRect& viewRect, SkRect& visibleRect,
+ float scale)
+{
+ XLOG("drawing BLA %p", this);
+
+ // TODO: consider moving drawBackground outside of prepare (into tree manager)
+ m_state->drawBackground(m_color);
+ drawBasePictureInGL();
+
+ bool needsRedraw = false;
+
+#if USE(ACCELERATED_COMPOSITING)
+
+ LayerAndroid* compositedRoot = static_cast<LayerAndroid*>(getChild(0));
+ if (compositedRoot) {
+ updateLayerPositions(visibleRect);
// For now, we render layers only if the rendering mode
// is kAllTextures or kClippedTextures
- if (m_glWebViewState->layersRenderingMode() < GLWebViewState::kScrollableAndFixedLayers
- && compositedRoot->drawGL(m_glWebViewState, matrix)) {
+ if (m_state->layersRenderingMode() < GLWebViewState::kScrollableAndFixedLayers
+ && compositedRoot->drawGL()) {
if (TilesManager::instance()->layerTexturesRemain()) {
// only try redrawing for layers if layer textures remain,
// otherwise we'll repaint without getting anything done
}
}
- m_previousVisible = visibleRect;
-
#endif // USE(ACCELERATED_COMPOSITING)
#ifdef DEBUG
ClassTracker::instance()->show();
virtual ~BaseLayerAndroid();
#if USE(ACCELERATED_COMPOSITING)
- void setGLWebViewState(GLWebViewState* infos) { m_glWebViewState = infos; }
void setBackgroundColor(Color& color) { m_color = color; }
Color getBackgroundColor() { return m_color; }
#endif
// we are running in different threads.
virtual bool drawCanvas(SkCanvas* canvas);
- bool drawGL(double currentTime, LayerAndroid* compositedRoot, IntRect& rect,
- SkRect& viewport, float scale, bool* buffersSwappedPtr);
+ void updateLayerPositions(SkRect& visibleRect);
+ bool prepare(double currentTime, IntRect& viewRect,
+ SkRect& visibleRect, float scale);
+ bool drawGL(IntRect& viewRect, SkRect& visibleRect, float scale);
+
+ // rendering asset management
+ void swapTiles();
+ void setIsDrawing(bool isDrawing);
+ void setIsPainting(Layer* drawingTree);
+ void mergeInvalsInto(Layer* replacementTree);
+ bool isReady();
+
private:
#if USE(ACCELERATED_COMPOSITING)
void prefetchBasePicture(SkRect& viewport, float currentScale,
TiledPage* prefetchTiledPage, bool draw);
- bool drawBasePictureInGL(SkRect& viewport, float scale, double currentTime,
- bool* buffersSwappedPtr);
+ bool prepareBasePictureInGL(SkRect& viewport, float scale, double currentTime);
+ void drawBasePictureInGL();
- GLWebViewState* m_glWebViewState;
android::Mutex m_drawLock;
Color m_color;
#endif
android::PictureSet m_content;
- SkRect m_previousVisible;
ScrollState m_scrollState;
};
#include "SkPath.h"
#include "TilesManager.h"
#include "TilesTracker.h"
+#include "TreeManager.h"
#include <wtf/CurrentTime.h>
#include <pthread.h>
GLWebViewState::GLWebViewState()
: m_zoomManager(this)
- , m_paintingBaseLayer(0)
- , m_currentBaseLayer(0)
- , m_currentBaseLayerRoot(0)
, m_currentPictureCounter(0)
, m_usePageA(true)
, m_frameworkInval(0, 0, 0, 0)
, m_frameworkLayersInval(0, 0, 0, 0)
- , m_baseLayerUpdate(true)
- , m_backgroundColor(SK_ColorWHITE)
, m_isScrolling(false)
, m_goingDown(true)
, m_goingLeft(false)
GLWebViewState::~GLWebViewState()
{
- // Unref the existing tree/PaintedSurfaces
- if (m_currentBaseLayerRoot)
- TilesManager::instance()->swapLayersTextures(m_currentBaseLayerRoot, 0);
-
// Take care of the transfer queue such that Tex Gen thread will not stuck
TilesManager::instance()->unregisterGLWebViewState(this);
// will remove any pending operations, and wait if one is underway).
delete m_tiledPageA;
delete m_tiledPageB;
- SkSafeUnref(m_paintingBaseLayer);
- SkSafeUnref(m_currentBaseLayer);
- SkSafeUnref(m_currentBaseLayerRoot);
- m_paintingBaseLayer = 0;
- m_currentBaseLayer = 0;
- m_currentBaseLayerRoot = 0;
#ifdef DEBUG_COUNT
ClassTracker::instance()->decrement("GLWebViewState");
#endif
void GLWebViewState::setBaseLayer(BaseLayerAndroid* layer, const SkRegion& inval,
bool showVisualIndicator, bool isPictureAfterFirstLayout)
{
- android::Mutex::Autolock lock(m_baseLayerLock);
if (!layer || isPictureAfterFirstLayout) {
+ // TODO: move this into TreeManager
m_tiledPageA->discardTextures();
m_tiledPageB->discardTextures();
}
- if (isPictureAfterFirstLayout) {
- m_baseLayerUpdate = true;
- m_invalidateRegion.setEmpty();
- }
-
- SkSafeRef(layer);
- SkSafeUnref(m_currentBaseLayer);
- m_currentBaseLayer = layer;
-
- // copy content from old composited root to new
- LayerAndroid* oldRoot = m_currentBaseLayerRoot;
if (layer) {
- layer->setGLWebViewState(this);
- m_currentBaseLayerRoot = static_cast<LayerAndroid*>(layer->getChild(0));
- SkSafeRef(m_currentBaseLayerRoot);
- } else {
- m_currentBaseLayerRoot = 0;
- }
- if (oldRoot != m_currentBaseLayerRoot)
- TilesManager::instance()->swapLayersTextures(oldRoot, m_currentBaseLayerRoot);
- SkSafeUnref(oldRoot);
-
- // We only update the base layer if we are not currently
- // waiting for a tiledPage to be painted
- if (m_baseLayerUpdate) {
- SkSafeRef(layer);
- SkSafeUnref(m_paintingBaseLayer);
- m_paintingBaseLayer = layer;
+ XLOG("new base layer %p, (inval region empty %d) with child %p", layer, inval.isEmpty(), layer->getChild(0));
+ layer->setState(this);
+ layer->markAsDirty(inval); // TODO: set in webview.cpp
}
+ m_treeManager.updateWithTree(layer, isPictureAfterFirstLayout);
m_glExtras.setDrawExtra(0);
- // TODO: do the union of both layers tree to compute
- // the minimum inval instead of doing a fullInval()
- if (m_layersRenderingMode == kSingleSurfaceRendering)
- fullInval();
- else
- invalRegion(inval);
-
#ifdef MEASURES_PERF
if (m_measurePerfs && !showVisualIndicator)
dumpMeasures();
void GLWebViewState::invalRegion(const SkRegion& region)
{
+ if (m_layersRenderingMode == kSingleSurfaceRendering) {
+ // TODO: do the union of both layers tree to compute
+ //the minimum inval instead of doing a fullInval()
+ fullInval();
+ return;
+ }
SkRegion::Iterator iterator(region);
while (!iterator.done()) {
SkIRect r = iterator.rect();
}
}
-void GLWebViewState::unlockBaseLayerUpdate() {
- if (m_baseLayerUpdate)
- return;
-
- m_baseLayerUpdate = true;
- android::Mutex::Autolock lock(m_baseLayerLock);
- SkSafeRef(m_currentBaseLayer);
- SkSafeUnref(m_paintingBaseLayer);
- m_paintingBaseLayer = m_currentBaseLayer;
-
- invalRegion(m_invalidateRegion);
- m_invalidateRegion.setEmpty();
-}
-
void GLWebViewState::inval(const IntRect& rect)
{
- if (m_baseLayerUpdate) {
- // base layer isn't locked, so go ahead and issue the inval to both tiled pages
- m_currentPictureCounter++;
- if (!rect.isEmpty()) {
- // find which tiles fall within the invalRect and mark them as dirty
- m_tiledPageA->invalidateRect(rect, m_currentPictureCounter);
- m_tiledPageB->invalidateRect(rect, m_currentPictureCounter);
- if (m_frameworkInval.isEmpty())
- m_frameworkInval = rect;
- else
- m_frameworkInval.unite(rect);
- XLOG("intermediate invalRect(%d, %d, %d, %d) after unite with rect %d %d %d %d", m_frameworkInval.x(),
- m_frameworkInval.y(), m_frameworkInval.width(), m_frameworkInval.height(),
- rect.x(), rect.y(), rect.width(), rect.height());
- }
- } else {
- // base layer is locked, so defer invalidation until unlockBaseLayerUpdate()
- m_invalidateRegion.op(rect.x(), rect.y(), rect.maxX(), rect.maxY(), SkRegion::kUnion_Op);
+ m_currentPictureCounter++;
+ if (!rect.isEmpty()) {
+ // find which tiles fall within the invalRect and mark them as dirty
+ m_tiledPageA->invalidateRect(rect, m_currentPictureCounter);
+ m_tiledPageB->invalidateRect(rect, m_currentPictureCounter);
+ if (m_frameworkInval.isEmpty())
+ m_frameworkInval = rect;
+ else
+ m_frameworkInval.unite(rect);
+ XLOG("intermediate invalRect(%d, %d, %d, %d) after unite with rect %d %d %d %d", m_frameworkInval.x(),
+ m_frameworkInval.y(), m_frameworkInval.width(), m_frameworkInval.height(),
+ rect.x(), rect.y(), rect.width(), rect.height());
}
TilesManager::instance()->getProfiler()->nextInval(rect, zoomManager()->currentScale());
}
unsigned int GLWebViewState::paintBaseLayerContent(SkCanvas* canvas)
{
- m_baseLayerLock.lock();
- BaseLayerAndroid* base = m_paintingBaseLayer;
- SkSafeRef(base);
- m_baseLayerLock.unlock();
- if (base) {
- base->drawCanvas(canvas);
- if (m_layersRenderingMode == kSingleSurfaceRendering) {
- LayerAndroid* rootLayer = static_cast<LayerAndroid*>(base->getChild(0));
- if (rootLayer)
- rootLayer->drawCanvas(canvas);
- }
- }
- SkSafeUnref(base);
+ m_treeManager.drawCanvas(canvas, m_layersRenderingMode == kSingleSurfaceRendering);
return m_currentPictureCounter;
}
int GLWebViewState::baseContentWidth()
{
- return m_paintingBaseLayer ? m_paintingBaseLayer->content()->width() : 0;
+ return m_treeManager.baseContentWidth();
}
int GLWebViewState::baseContentHeight()
{
- return m_paintingBaseLayer ? m_paintingBaseLayer->content()->height() : 0;
+ return m_treeManager.baseContentHeight();
}
void GLWebViewState::setViewport(SkRect& viewport, float scale)
m_frameworkLayersInval.setHeight(0);
}
-double GLWebViewState::setupDrawing(IntRect& viewRect, SkRect& visibleRect,
- IntRect& webViewRect, int titleBarHeight,
- IntRect& screenClip, float scale)
+void GLWebViewState::drawBackground(Color& backgroundColor)
{
- int left = viewRect.x();
- int top = viewRect.y();
- int width = viewRect.width();
- int height = viewRect.height();
-
if (TilesManager::instance()->invertedScreen()) {
- float color = 1.0 - ((((float) m_backgroundColor.red() / 255.0) +
- ((float) m_backgroundColor.green() / 255.0) +
- ((float) m_backgroundColor.blue() / 255.0)) / 3.0);
+ float color = 1.0 - ((((float) backgroundColor.red() / 255.0) +
+ ((float) backgroundColor.green() / 255.0) +
+ ((float) backgroundColor.blue() / 255.0)) / 3.0);
glClearColor(color, color, color, 1);
} else {
- glClearColor((float)m_backgroundColor.red() / 255.0,
- (float)m_backgroundColor.green() / 255.0,
- (float)m_backgroundColor.blue() / 255.0, 1);
+ glClearColor((float)backgroundColor.red() / 255.0,
+ (float)backgroundColor.green() / 255.0,
+ (float)backgroundColor.blue() / 255.0, 1);
}
glClear(GL_COLOR_BUFFER_BIT);
+}
+double GLWebViewState::setupDrawing(IntRect& viewRect, SkRect& visibleRect,
+ IntRect& webViewRect, int titleBarHeight,
+ IntRect& screenClip, float scale)
+{
+ int left = viewRect.x();
+ int top = viewRect.y();
+ int width = viewRect.width();
+ int height = viewRect.height();
glViewport(left, top, width, height);
ShaderProgram* shader = TilesManager::instance()->shader();
TilesManager::instance()->getTilesTracker()->clear();
#endif
- m_baseLayerLock.lock();
- BaseLayerAndroid* baseLayer = m_paintingBaseLayer;
- SkSafeRef(baseLayer);
- m_baseLayerLock.unlock();
- if (!baseLayer)
- return false;
-
float viewWidth = (viewport.fRight - viewport.fLeft) * TILE_PREFETCH_RATIO;
float viewHeight = (viewport.fBottom - viewport.fTop) * TILE_PREFETCH_RATIO;
bool useMinimalMemory = TilesManager::instance()->useMinimalMemory();
resetLayersDirtyArea();
- LayerAndroid* compositedRoot = m_currentBaseLayerRoot;
- LayerAndroid* paintingBaseLayerRoot = 0;
- if (baseLayer && baseLayer->countChildren() >= 1)
- paintingBaseLayerRoot = static_cast<LayerAndroid*>(baseLayer->getChild(0));
-
// when adding or removing layers, use the the paintingBaseLayer's tree so
// that content that moves to the base layer from a layer is synchronized
- bool paintingHasLayers = (paintingBaseLayerRoot == 0);
- bool currentHasLayers = (m_currentBaseLayerRoot == 0);
- if (paintingHasLayers != currentHasLayers)
- compositedRoot = paintingBaseLayerRoot;
if (scale < MIN_SCALE_WARNING || scale > MAX_SCALE_WARNING)
XLOGC("WARNING, scale seems corrupted before update: %e", scale);
// gather the textures we can use
TilesManager::instance()->gatherLayerTextures();
- // set up zoom manager, shaders, etc.
- m_backgroundColor = baseLayer->getBackgroundColor();
double currentTime = setupDrawing(rect, viewport, webViewRect, titleBarHeight, clip, scale);
- if (compositedRoot)
- compositedRoot->setState(this);
- bool animsRunning = false;
TexturesResult nbTexturesNeeded;
- if (compositedRoot) {
- TransformationMatrix ident;
- animsRunning = compositedRoot->evaluateAnimations();
- bool hasFixedElements = compositedRoot->updateFixedLayersPositions(viewport);
- if (m_layersRenderingMode == kSingleSurfaceRendering) {
- // If we are in single surface rendering, we may have to fully
- // invalidate if we have fixed elements or if we have CSS
- // animations.
- // TODO: compute the minimum invals
- if (animsRunning || hasFixedElements)
- fullInval();
- }
-
- // TODO: get the base document area for the original clip
- FloatRect clip(0, 0, 1E6, 1E6);
- compositedRoot->updateGLPositionsAndScale(ident, clip, 1, zoomManager()->layersScale());
- compositedRoot->prepare(this);
-
- ret |= animsRunning;
+ bool fastSwap = isScrolling() || m_layersRenderingMode == kSingleSurfaceRendering;
+ ret |= m_treeManager.drawGL(currentTime, rect, viewport,
+ scale, fastSwap,
+ buffersSwappedPtr, &nbTexturesNeeded);
+ if (!ret)
+ resetFrameworkInval();
- compositedRoot->computeTexturesAmount(&nbTexturesNeeded);
- }
ret |= setLayersRenderingMode(nbTexturesNeeded);
- // Clean up GL textures for video layer.
- TilesManager::instance()->videoLayerManager()->deleteUnusedTextures();
-
- ret |= baseLayer->drawGL(currentTime, compositedRoot, rect,
- viewport, scale, buffersSwappedPtr);
-
FloatRect extrasclip(0, 0, rect.width(), rect.height());
TilesManager::instance()->shader()->clip(extrasclip);
glBindBuffer(GL_ARRAY_BUFFER, 0);
+ // Clean up GL textures for video layer.
+ TilesManager::instance()->videoLayerManager()->deleteUnusedTextures();
ret |= TilesManager::instance()->invertedScreenSwitch();
if (ret) {
}
#endif
- SkSafeUnref(baseLayer);
#ifdef DEBUG
TilesManager::instance()->getTilesTracker()->showTrackTextures();
ImagesManager::instance()->showImages();
#include "SkRect.h"
#include "SkRegion.h"
#include "TiledPage.h"
+#include "TreeManager.h"
#include "ZoomManager.h"
#include <utils/threads.h>
unsigned int currentPictureCounter() const { return m_currentPictureCounter; }
- void lockBaseLayerUpdate() { m_baseLayerUpdate = false; }
- void unlockBaseLayerUpdate();
-
void setIsScrolling(bool isScrolling) { m_isScrolling = isScrolling; }
bool isScrolling() { return m_isScrolling; }
- double setupDrawing(IntRect& rect, SkRect& viewport, IntRect& webViewRect,
- int titleBarHeight, IntRect& screenClip,
- float scale);
+ void drawBackground(Color& backgroundColor);
+ double setupDrawing(IntRect& viewRect, SkRect& visibleRect,
+ IntRect& webViewRect, int titleBarHeight,
+ IntRect& screenClip, float scale);
bool setLayersRenderingMode(TexturesResult&);
void fullInval();
LayersRenderingMode layersRenderingMode() { return m_layersRenderingMode; }
void scrolledLayer(ScrollableLayerAndroid*);
-private:
- void inval(const IntRect& rect); // caller must hold m_baseLayerLock
void invalRegion(const SkRegion& region);
+private:
+ void inval(const IntRect& rect);
+
ZoomManager m_zoomManager;
android::Mutex m_tiledPageLock;
SkRect m_viewport;
SkIRect m_viewportTileBounds;
SkIRect m_futureViewportTileBounds;
SkIRect m_preZoomBounds;
- android::Mutex m_baseLayerLock;
- BaseLayerAndroid* m_paintingBaseLayer;
- BaseLayerAndroid* m_currentBaseLayer;
- LayerAndroid* m_currentBaseLayerRoot;
unsigned int m_currentPictureCounter;
bool m_usePageA;
IntRect m_frameworkInval;
IntRect m_frameworkLayersInval;
- bool m_baseLayerUpdate;
- SkRegion m_invalidateRegion;
-
- Color m_backgroundColor;
-
#ifdef MEASURES_PERF
unsigned int m_totalTimeCounter;
int m_timeCounter;
float m_scale;
LayersRenderingMode m_layersRenderingMode;
+ TreeManager m_treeManager;
};
} // namespace WebCore
#ifndef ImageTexture_h
#define ImageTexture_h
-#include "ClassTracker.h"
#include "GLUtils.h"
#include "SkBitmap.h"
#include "SkBitmapRef.h"
m_shouldInheritFromRootTransform = false;
m_hasOverflowChildren = false;
+ m_state = 0;
#ifdef DEBUG_TRACK_NEW_DELETE
gLayerAllocCount += 1;
m_shouldInheritFromRootTransform = src.m_shouldInheritFromRootTransform;
m_hasOverflowChildren = src.m_hasOverflowChildren;
+ m_state = 0;
#ifdef DEBUG_TRACK_NEW_DELETE
gLayerAllocCount += 1;
}
return false;
}
+
+void Layer::setState(WebCore::GLWebViewState* state) {
+ m_state = state;
+ int count = countChildren();
+ for (int i = 0; i < count; i++)
+ m_children[i]->setState(state);
+}
#ifndef Layer_DEFINED
#define Layer_DEFINED
+#include "TestExport.h"
#include "SkRefCnt.h"
#include "SkTDArray.h"
#include "SkColor.h"
#include "SkMatrix.h"
#include "SkPoint.h"
#include "SkRect.h"
+#include "SkRegion.h"
#include "SkSize.h"
+namespace WebCore {
+class IntRect;
+class GLWebViewState;
+};
+
class SkCanvas;
-class Layer : public SkRefCnt {
+class TEST_EXPORT Layer : public SkRefCnt {
public:
Layer();
void setMatrix(const SkMatrix& matrix) { m_matrix = matrix; }
void setChildrenMatrix(const SkMatrix& matrix) { m_childrenMatrix = matrix; }
- // children
+// rendering asset management
+
+ // tell rendering assets to update their tile content with most recent painted data
+ virtual void swapTiles() {}
+
+ // tell rendering assets to use this layer tree for drawing
+ virtual void setIsDrawing(bool isDrawing) {}
+
+ // take rendering assets from drawing tree, or create if they don't exist
+ virtual void setIsPainting(Layer* drawingTree) {}
+
+ // if a similar layer exists in the replacement tree, add invals to it
+ virtual void mergeInvalsInto(Layer* replacementTree) {}
+
+ void markAsDirty(const SkRegion& invalRegion) {
+ m_dirtyRegion.op(invalRegion, SkRegion::kUnion_Op);
+ }
+
+ bool isDirty() {
+ return !m_dirtyRegion.isEmpty();
+ }
+
+// drawing
+
+ virtual bool isReady() { return false; }
+
+ // TODO: clean out several of these, leave them in GLWebViewState
+
+ virtual bool prepare(double currentTime, WebCore::IntRect& viewRect,
+ SkRect& visibleRect, float scale) { return 0; }
+ virtual bool drawGL(WebCore::IntRect& viewRect,
+ SkRect& visibleRect, float scale) { return 0; }
+ WebCore::GLWebViewState* state() { return m_state; }
+ void setState(WebCore::GLWebViewState* state);
+
+// children
/** Return the number of layers in our child list.
*/
SkTDArray<Layer*> m_children;
+ // invalidation region
+ SkRegion m_dirtyRegion;
+
+ WebCore::GLWebViewState* m_state;
+
typedef SkRefCnt INHERITED;
};
m_imageRef(0),
m_imageTexture(0),
m_pictureUsed(0),
- m_requestSent(false),
m_scale(1),
m_lastComputeTextureSize(0),
m_owningLayer(owner),
m_type(LayerAndroid::WebCoreLayer),
- m_state(0),
m_hasText(true)
{
m_backgroundColor = 0;
m_preserves3D = false;
- m_dirty = false;
m_iframeOffset.set(0,0);
m_dirtyRegion.setEmpty();
#ifdef DEBUG_COUNT
m_uniqueId(layer.m_uniqueId),
m_texture(0),
m_imageTexture(0),
- m_requestSent(false),
m_owningLayer(layer.m_owningLayer),
m_type(LayerAndroid::UILayer),
- m_state(0),
m_hasText(true)
{
m_isFixed = layer.m_isFixed;
m_anchorPointZ = layer.m_anchorPointZ;
m_drawTransform = layer.m_drawTransform;
m_childrenTransform = layer.m_childrenTransform;
- m_dirty = layer.m_dirty;
m_pictureUsed = layer.m_pictureUsed;
m_dirtyRegion = layer.m_dirtyRegion;
m_scale = layer.m_scale;
m_isFixed(false),
m_isIframe(false),
m_recordingPicture(picture),
- m_uniqueId(-1),
+ m_uniqueId(++gUniqueId),
m_texture(0),
m_imageRef(0),
m_imageTexture(0),
- m_requestSent(false),
m_scale(1),
m_lastComputeTextureSize(0),
m_owningLayer(0),
m_type(LayerAndroid::NavCacheLayer),
- m_state(0),
m_hasText(true)
{
m_backgroundColor = 0;
- m_dirty = false;
SkSafeRef(m_recordingPicture);
m_iframeOffset.set(0,0);
m_dirtyRegion.setEmpty();
return hasRunningAnimations || m_hasRunningAnimations;
}
-void LayerAndroid::addDirtyArea(GLWebViewState* glWebViewState)
+void LayerAndroid::addDirtyArea()
{
IntSize layerSize(getSize().width(), getSize().height());
area.intersect(clip);
IntRect dirtyArea(area.x(), area.y(), area.width(), area.height());
- glWebViewState->addDirtyArea(dirtyArea);
+ m_state->addDirtyArea(dirtyArea);
}
void LayerAndroid::addAnimation(PassRefPtr<AndroidAnimation> prpAnim)
void LayerAndroid::computeTexturesAmount(TexturesResult* result)
{
+ if (!result)
+ return;
+
int count = this->countChildren();
for (int i = 0; i < count; i++)
this->getChild(i)->computeTexturesAmount(result);
this->getChild(i)->showLayer(indent + 1);
}
-// We go through our tree, and if we have layer in the new
-// tree that is similar, we transfer our texture to it.
-// Otherwise, we remove ourselves from the texture so
-// that TilesManager::swapLayersTextures() have a chance
-// at deallocating the textures (PaintedSurfaces)
-void LayerAndroid::assignTextureTo(LayerAndroid* newTree)
+void LayerAndroid::swapTiles()
{
int count = this->countChildren();
for (int i = 0; i < count; i++)
- this->getChild(i)->assignTextureTo(newTree);
+ this->getChild(i)->swapTiles();
- if (newTree) {
- LayerAndroid* newLayer = newTree->findById(uniqueId());
- if (newLayer == this)
- return;
- if (newLayer && m_texture) {
- m_texture->replaceLayer(newLayer);
- newLayer->m_texture = m_texture;
- m_texture = 0;
- }
- if (!newLayer && m_texture) {
- m_texture->removeLayer(this);
- m_texture = 0;
- }
+ if (m_texture)
+ m_texture->swapTiles();
+}
+
+void LayerAndroid::setIsDrawing(bool isDrawing)
+{
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ this->getChild(i)->setIsDrawing(isDrawing);
+
+ if (m_texture) {
+ m_texture->setDrawingLayer(isDrawing ? this : 0);
+ m_texture->clearPaintingLayer();
}
}
+void LayerAndroid::setIsPainting(Layer* drawingTree)
+{
+ XLOG("setting layer %p as painting, needs texture %d, drawing tree %p",
+ this, needsTexture(), drawingTree);
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ this->getChild(i)->setIsPainting(drawingTree);
+
+ obtainTextureForPainting(static_cast<LayerAndroid*>(drawingTree));
+}
+
+void LayerAndroid::mergeInvalsInto(Layer* replacementTree)
+{
+ int count = this->countChildren();
+ for (int i = 0; i < count; i++)
+ this->getChild(i)->mergeInvalsInto(replacementTree);
+
+ LayerAndroid* replacementLayer = static_cast<LayerAndroid*>(replacementTree)->findById(uniqueId());
+ if (replacementLayer)
+ replacementLayer->markAsDirty(m_dirtyRegion);
+}
+
+bool LayerAndroid::isReady()
+{
+ int count = countChildren();
+ for (int i = 0; i < count; i++)
+ if (!getChild(i)->isReady())
+ return false;
+
+ if (m_texture)
+ return m_texture->isReady();
+ // TODO: image, check if uploaded?
+ return true;
+}
+
bool LayerAndroid::updateWithTree(LayerAndroid* newTree)
{
// Disable fast update for now
return false;
}
-void LayerAndroid::createTexture()
+void LayerAndroid::obtainTextureForPainting(LayerAndroid* drawingTree)
{
- int count = this->countChildren();
- for (int i = 0; i < count; i++)
- this->getChild(i)->createTexture();
-
if (!needsTexture())
return;
m_dirtyRegion.setEmpty();
}
if (m_texture) {
- m_texture->removeLayer(this);
+ m_texture->setDrawingLayer(0);
+ m_texture->clearPaintingLayer();
m_texture = 0;
}
} else {
+ if (drawingTree) {
+ LayerAndroid* drawingLayer = drawingTree->findById(uniqueId());
+ if (drawingLayer) {
+ // if a previous tree had the same layer, paint with that painted surface
+ m_texture = drawingLayer->m_texture;
+ }
+ }
+
if (!m_texture)
- m_texture = new PaintedSurface(this);
+ m_texture = new PaintedSurface();
// pass the invalidated regions to the PaintedSurface
- m_texture->markAsDirty(m_dirtyRegion);
+ m_texture->setPaintingLayer(this, m_dirtyRegion);
m_dirtyRegion.setEmpty();
}
}
+
static inline bool compareLayerZ(const LayerAndroid* a, const LayerAndroid* b)
{
return a->zValue() > b->zValue();
}
-void LayerAndroid::markAsDirty(const SkRegion& dirtyArea)
-{
- m_dirtyRegion.op(dirtyArea, SkRegion::kUnion_Op);
-}
-
// We call this in WebViewCore, when copying the tree of layers.
// As we construct a new tree that will be passed on the UI,
// we mark the webkit-side tree as having no more dirty region
m_dirtyRegion.setEmpty();
}
-void LayerAndroid::setState(GLWebViewState* state)
-{
- int count = this->countChildren();
- for (int i = 0; i < count; i++)
- this->getChild(i)->setState(state);
-
- m_state = state;
-}
-
-void LayerAndroid::prepare(GLWebViewState* glWebViewState)
+void LayerAndroid::prepare()
{
- m_state = glWebViewState;
+ XLOG("LA %p preparing, m_texture %p", this, m_texture);
int count = this->countChildren();
if (count > 0) {
// iterate in reverse so top layers get textures first
for (int i = count-1; i >= 0; i--)
- sublayers[i]->prepare(glWebViewState);
+ sublayers[i]->prepare();
}
if (m_texture)
- m_texture->prepare(glWebViewState);
+ m_texture->prepare(m_state);
if (m_imageTexture)
m_imageTexture->prepareGL();
// When the layer is dirty, the UI thread should be notified to redraw.
askScreenUpdate |= drawChildrenCanvas(canvas);
m_atomicSync.lock();
- askScreenUpdate |= m_dirty;
if (askScreenUpdate || m_hasRunningAnimations || m_drawTransform.hasPerspective())
- addDirtyArea(m_state);
+ addDirtyArea();
m_atomicSync.unlock();
return askScreenUpdate;
}
-bool LayerAndroid::drawGL(GLWebViewState* glWebViewState, SkMatrix& matrix)
+bool LayerAndroid::drawGL()
{
FloatRect clippingRect = TilesManager::instance()->shader()->rectInScreenCoord(m_clippingRect);
TilesManager::instance()->shader()->clip(clippingRect);
m_imageTexture->drawGL(this);
// When the layer is dirty, the UI thread should be notified to redraw.
- askScreenUpdate |= drawChildrenGL(glWebViewState, matrix);
+ askScreenUpdate |= drawChildrenGL();
m_atomicSync.lock();
- askScreenUpdate |= m_dirty;
if (askScreenUpdate || m_hasRunningAnimations || m_drawTransform.hasPerspective())
- addDirtyArea(glWebViewState);
+ addDirtyArea();
m_atomicSync.unlock();
return askScreenUpdate;
return askScreenUpdate;
}
-bool LayerAndroid::drawChildrenGL(GLWebViewState* glWebViewState, SkMatrix& matrix)
+bool LayerAndroid::drawChildrenGL()
{
bool askScreenUpdate = false;
int count = this->countChildren();
std::stable_sort(sublayers.begin(), sublayers.end(), compareLayerZ);
for (int i = 0; i < count; i++) {
LayerAndroid* layer = sublayers[i];
- askScreenUpdate |= layer->drawGL(glWebViewState, matrix);
+ askScreenUpdate |= layer->drawGL();
}
}
#include "SkColor.h"
#include "SkRegion.h"
#include "SkStream.h"
-#include "TextureOwner.h"
#include "TransformationMatrix.h"
#include <wtf/HashMap.h>
-#include <wtf/text/StringHash.h>
#ifndef BZERO_DEFINED
#define BZERO_DEFINED
class AndroidAnimation;
class BaseTileTexture;
+class GLWebViewState;
class LayerAndroidFindState;
class RenderLayer;
class TiledPage;
int full;
};
-class LayerAndroid : public Layer {
+class TEST_EXPORT LayerAndroid : public Layer {
public:
enum LayerType { UndefinedLayer, WebCoreLayer, UILayer, NavCacheLayer };
virtual ~LayerAndroid();
virtual TiledPage* page() { return 0; }
- virtual GLWebViewState* state() { return m_state; }
void setBackfaceVisibility(bool value) { m_backfaceVisibility = value; }
void setTransform(const TransformationMatrix& matrix) { m_transform = matrix; }
// Debug helper methods
int nbLayers();
int nbTexturedLayers();
- void showLayer(int indent);
+ void showLayer(int indent = 0);
void computeTexturesAmount(TexturesResult*);
float getScale() { return m_scale; }
- void setState(GLWebViewState*);
-
// draw layer and its children via Z, pre-order traversal
- virtual bool drawGL(GLWebViewState*, SkMatrix&);
- bool drawChildrenGL(GLWebViewState*, SkMatrix&);
+ virtual bool drawGL();
+ bool drawChildrenGL();
virtual bool drawCanvas(SkCanvas*);
bool drawChildrenCanvas(SkCanvas*);
// prepare layer and its children via reverse-Z, post-order traversal
- void prepare(GLWebViewState*);
+ void prepare();
void updateGLPositionsAndScale(const TransformationMatrix& parentMatrix,
const FloatRect& clip, float opacity, float scale);
bool evaluateAnimations();
bool evaluateAnimations(double time);
bool hasAnimations() const;
- void addDirtyArea(GLWebViewState*);
+ void addDirtyArea();
SkPicture* picture() const { return m_recordingPicture; }
void needsRepaint() { m_pictureUsed++; }
unsigned int pictureUsed() { return m_pictureUsed; }
- void markAsDirty(const SkRegion& dirtyArea);
void clearDirtyRegion();
- const SkRegion& dirtyRegion() { return m_dirtyRegion; }
void contentDraw(SkCanvas*);
friend void android::cleanupImageRefs(LayerAndroid* layer);
PaintedSurface* texture() { return m_texture; }
- void assignTextureTo(LayerAndroid* newTree);
- void createTexture();
+ void obtainTextureForPainting(LayerAndroid* drawingTree);
// Update layers using another tree. Only works for basic properties
// such as the position, the transform. Return true if anything more
bool hasText() { return m_hasText; }
void checkTextPresence();
+// rendering asset management
+ void swapTiles();
+ void setIsDrawing(bool isDrawing);
+ void setIsPainting(Layer* drawingTree);
+ void mergeInvalsInto(Layer* replacementTree);
+ bool isReady();
+
protected:
virtual void onDraw(SkCanvas*, SkScalar opacity);
SkBitmapRef* m_imageRef;
ImageTexture* m_imageTexture;
- // used to signal that the tile is out-of-date and needs to be redrawn
- bool m_dirty;
unsigned int m_pictureUsed;
- // dirty regions
- SkRegion m_dirtyRegion;
-
// used to signal the framework we need a repaint
bool m_hasRunningAnimations;
- // painting request sent
- bool m_requestSent;
-
float m_scale;
// We try to not always compute the texture size, as this is quite heavy
RenderLayer* m_owningLayer;
int m_type;
- GLWebViewState* m_state;
bool m_hasText;
m_mediaTexture->decStrong(this);
}
-bool MediaLayer::drawGL(GLWebViewState* glWebViewState, SkMatrix& matrix)
+bool MediaLayer::drawGL()
{
FloatRect clippingRect = TilesManager::instance()->shader()->rectInScreenCoord(drawClip());
TilesManager::instance()->shader()->clip(clippingRect);
// draw any content or video if present
m_mediaTexture->draw(m, m_drawTransform, mediaBounds);
- return drawChildrenGL(glWebViewState, matrix);
+ return drawChildrenGL();
}
ANativeWindow* MediaLayer::acquireNativeWindowForContent()
MediaLayer(const MediaLayer& layer);
virtual ~MediaLayer();
- virtual bool drawGL(GLWebViewState*, SkMatrix&);
+ virtual bool drawGL();
virtual void paintBitmapGL() const { };
virtual bool needsTexture() { return false; }
: QueuedOperation(QueuedOperation::PaintTile, tile->page())
, m_tile(tile)
, m_surface(surface)
- , m_layer(0)
{
if (m_tile)
m_tile->setRepaintPending(true);
- if (m_surface)
- m_layer = surface->layer();
SkSafeRef(m_surface);
- SkSafeRef(m_layer);
}
PaintTileOperation::~PaintTileOperation()
m_tile = 0;
}
SkSafeUnref(m_surface);
- SkSafeUnref(m_layer);
}
bool PaintTileOperation::operator==(const QueuedOperation* operation)
// for base tiles, prioritize based on position
if (!m_tile->isLayerTile()) {
bool goingDown = m_tile->page()->scrollingDown();
- SkIRect *rect = m_tile->page()->expandedTileBounds();
priority += m_tile->x();
if (goingDown)
#ifndef PaintTileSetOperation_h
#define PaintTileSetOperation_h
+#include "BaseTile.h"
#include "QueuedOperation.h"
namespace WebCore {
private:
BaseTile* m_tile;
PaintedSurface* m_surface;
- LayerAndroid* m_layer;
};
class ScaleFilter : public OperationFilter {
#include "LayerAndroid.h"
+#include "TiledTexture.h"
#include "TilesManager.h"
#include "SkCanvas.h"
#include "SkPicture.h"
namespace WebCore {
-PaintedSurface::PaintedSurface(LayerAndroid* layer)
- : m_layer(layer)
+PaintedSurface::PaintedSurface()
+ : m_drawingLayer(0)
+ , m_paintingLayer(0)
, m_tiledTexture(0)
, m_scale(0)
, m_pictureUsed(0)
{
TilesManager::instance()->addPaintedSurface(this);
- SkSafeRef(m_layer);
#ifdef DEBUG_COUNT
ClassTracker::instance()->increment("PaintedSurface");
#endif
m_tiledTexture = new DualTiledTexture(this);
- if (layer && layer->picture())
- m_updateManager.updatePicture(layer->picture());
}
PaintedSurface::~PaintedSurface()
{
- XLOG("dtor of %x m_layer: %x", this, m_layer);
- android::Mutex::Autolock lock(m_layerLock);
- SkSafeUnref(m_layer);
#ifdef DEBUG_COUNT
ClassTracker::instance()->decrement("PaintedSurface");
#endif
delete m_tiledTexture;
}
-void PaintedSurface::removeLayer()
-{
- android::Mutex::Autolock lock(m_layerLock);
- if (m_layer)
- m_layer->removeTexture(this);
- SkSafeUnref(m_layer);
- m_layer = 0;
-}
-
-void PaintedSurface::removeLayer(LayerAndroid* layer)
-{
- android::Mutex::Autolock lock(m_layerLock);
- if (m_layer != layer)
- return;
- SkSafeUnref(m_layer);
- m_layer = 0;
-}
-
-void PaintedSurface::replaceLayer(LayerAndroid* layer)
-{
- android::Mutex::Autolock lock(m_layerLock);
- if (!layer)
- return;
-
- if (m_layer && layer->uniqueId() != m_layer->uniqueId())
- return;
-
- SkSafeRef(layer);
- SkSafeUnref(m_layer);
- m_layer = layer;
- if (layer && layer->picture())
- m_updateManager.updatePicture(layer->picture());
-}
-
void PaintedSurface::prepare(GLWebViewState* state)
{
- if (!m_layer)
- return;
+ XLOG("PS %p has PL %p, DL %p", this, m_paintingLayer, m_drawingLayer);
+ LayerAndroid* paintingLayer = m_paintingLayer;
+ if (!paintingLayer)
+ paintingLayer = m_drawingLayer;
- if (!m_layer->needsTexture())
+ if (!paintingLayer)
return;
bool startFastSwap = false;
if (state->isScrolling()) {
// when scrolling, block updates and swap tiles as soon as they're ready
startFastSwap = true;
- } else {
- // when not, push updates down to TiledTexture in every prepare
- m_updateManager.swap();
- m_tiledTexture->update(m_updateManager.getPaintingInval(),
- m_updateManager.getPaintingPicture());
- m_updateManager.clearPaintingInval();
}
XLOG("prepare layer %d %x at scale %.2f",
- m_layer->uniqueId(), m_layer,
- m_layer->getScale());
+ paintingLayer->uniqueId(), paintingLayer,
+ paintingLayer->getScale());
- IntRect visibleArea = computeVisibleArea(m_layer);
+ IntRect visibleArea = computeVisibleArea(paintingLayer);
m_scale = state->scale();
// If we do not have text, we may as well limit ourselves to
// a scale factor of one... this saves up textures.
- if (m_scale > 1 && !m_layer->hasText())
+ if (m_scale > 1 && !paintingLayer->hasText())
m_scale = 1;
- m_tiledTexture->prepare(state, m_scale, m_pictureUsed != m_layer->pictureUsed(),
+ m_tiledTexture->prepare(state, m_scale, m_pictureUsed != paintingLayer->pictureUsed(),
startFastSwap, visibleArea);
}
bool PaintedSurface::draw()
{
- if (!m_layer || !m_layer->needsTexture())
+ if (!m_drawingLayer || !m_drawingLayer->needsTexture())
return false;
bool askRedraw = false;
return askRedraw;
}
-void PaintedSurface::markAsDirty(const SkRegion& dirtyArea)
+void PaintedSurface::setPaintingLayer(LayerAndroid* layer, const SkRegion& dirtyArea)
+{
+ m_paintingLayer = layer;
+ if (m_tiledTexture)
+ m_tiledTexture->update(dirtyArea, layer->picture());
+}
+
+bool PaintedSurface::isReady()
+{
+ if (m_tiledTexture)
+ return m_tiledTexture->isReady();
+ return false;
+}
+
+void PaintedSurface::swapTiles()
{
- m_updateManager.updateInval(dirtyArea);
+ if (m_tiledTexture)
+ m_tiledTexture->swapTiles();
}
float PaintedSurface::opacity() {
- if (m_layer)
- return m_layer->drawOpacity();
+ if (m_drawingLayer)
+ return m_drawingLayer->drawOpacity();
return 1.0;
}
const TransformationMatrix* PaintedSurface::transform() {
- if (!m_layer)
+ // used exclusively for drawing, so only use m_drawingLayer
+ if (!m_drawingLayer)
return 0;
- return m_layer->drawTransform();
+ return m_drawingLayer->drawTransform();
}
void PaintedSurface::computeTexturesAmount(TexturesResult* result)
if (!m_tiledTexture)
return;
- if (!m_layer)
+ // for now, always done on drawinglayer
+ LayerAndroid* layer = m_drawingLayer;
+
+ if (!layer)
return;
- IntRect unclippedArea = m_layer->unclippedArea();
- IntRect clippedVisibleArea = m_layer->visibleArea();
+ IntRect unclippedArea = layer->unclippedArea();
+ IntRect clippedVisibleArea = layer->visibleArea();
// get two numbers here:
// - textures needed for a clipped area
// - textures needed for an un-clipped area
int nbTexturesClipped = m_tiledTexture->nbTextures(clippedVisibleArea, m_scale);
// Set kFixedLayers level
- if (m_layer->isFixed())
+ if (layer->isFixed())
result->fixed += nbTexturesClipped;
// Set kScrollableAndFixedLayers level
- if (m_layer->contentIsScrollable()
- || m_layer->isFixed())
+ if (layer->contentIsScrollable()
+ || layer->isFixed())
result->scrollable += nbTexturesClipped;
// Set kClippedTextures level
result->clipped += nbTexturesClipped;
// Set kAllTextures level
- if (m_layer->contentIsScrollable())
+ if (layer->contentIsScrollable())
result->full += nbTexturesClipped;
else
result->full += nbTexturesUnclipped;
#include "LayerAndroid.h"
#include "SkRefCnt.h"
#include "TextureOwner.h"
-#include "TiledTexture.h"
#include "TilesManager.h"
#include "TilePainter.h"
#include "TransformationMatrix.h"
-#include "UpdateManager.h"
class SkCanvas;
class SkRegion;
namespace WebCore {
-class UpdateManager;
+class DualTiledTexture;
class PaintedSurface : public SkRefCnt {
public:
- PaintedSurface(LayerAndroid* layer);
+ PaintedSurface();
virtual ~PaintedSurface();
// PaintedSurface methods
- LayerAndroid* layer() { return m_layer; }
void prepare(GLWebViewState*);
bool draw();
- void markAsDirty(const SkRegion& dirtyArea);
bool paint(SkCanvas*);
- void removeLayer();
- void removeLayer(LayerAndroid* layer);
- void replaceLayer(LayerAndroid* layer);
+
+ void setDrawingLayer(LayerAndroid* layer) { m_drawingLayer = layer; }
+ LayerAndroid* drawingLayer() { return m_drawingLayer; }
+
+ void setPaintingLayer(LayerAndroid* layer, const SkRegion& dirtyArea);
+ void clearPaintingLayer() { m_paintingLayer = 0; }
+ LayerAndroid* paintingLayer() { return m_paintingLayer; }
+
+ void swapTiles();
+ bool isReady();
bool owns(BaseTileTexture* texture);
unsigned int pictureUsed() { return m_pictureUsed; }
private:
- UpdateManager m_updateManager;
-
- LayerAndroid* m_layer;
+ LayerAndroid* m_drawingLayer;
+ LayerAndroid* m_paintingLayer;
DualTiledTexture* m_tiledTexture;
float m_scale;
#ifndef QueuedOperation_h
#define QueuedOperation_h
-#include "TiledPage.h"
-
namespace WebCore {
+class TiledPage;
+
class QueuedOperation {
public:
enum OperationType { Undefined, PaintTile, PaintLayer, DeleteTexture };
--- /dev/null
+/*
+ * Copyright 2011, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TestExport_h
+#define TestExport_h
+
+// classes used outside of the .so, in tests, are declared with this attribute
+#define TEST_EXPORT __attribute__((visibility("default")))
+
+#endif // #define TestExport_h
, m_latestPictureInval(0)
, m_prepare(false)
, m_isPrefetchPage(false)
+ , m_willDraw(false)
{
m_baseTiles = new BaseTile[TilesManager::getMaxTextureAllocation() + 1];
#ifdef DEBUG_COUNT
lastTileX = std::min(lastTileX, static_cast<int>(ceilf(maxWidthTiles)) - 1);
lastTileY = std::min(lastTileY, static_cast<int>(ceilf(maxHeightTiles)) - 1);
- m_expandedTileBounds.fLeft = firstTileX;
- m_expandedTileBounds.fTop = firstTileY;
- m_expandedTileBounds.fRight = lastTileX;
- m_expandedTileBounds.fBottom = lastTileY;
-
// check against corrupted scale values giving bad height/width (use float to avoid overflow)
float numTiles = static_cast<float>(nbTilesHeight) * static_cast<float>(nbTilesWidth);
if (numTiles > TilesManager::getMaxTextureAllocation() || nbTilesHeight < 1 || nbTilesWidth < 1)
return neededTiles > 0;
}
-bool TiledPage::swapBuffersIfReady(const SkIRect& tileBounds, float scale, SwapMethod swap)
+bool TiledPage::isReady(const SkIRect& tileBounds)
+{
+ int neededTiles = tileBounds.width() * tileBounds.height();
+ XLOG("tiled page %p needs %d ready tiles", this, neededTiles);
+ for (int j = 0; j < m_baseTileSize; j++) {
+ BaseTile& tile = m_baseTiles[j];
+ if (tileBounds.contains(tile.x(), tile.y())) {
+ if (tile.isTileReady())
+ neededTiles--;
+ }
+ }
+ XLOG("tiled page %p still needs %d ready tiles", this, neededTiles);
+ return neededTiles == 0;
+}
+
+bool TiledPage::swapBuffersIfReady(const SkIRect& tileBounds, float scale)
{
if (!m_glWebViewState)
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++) {
- BaseTile* t = getBaseTile(x, y);
- if (!t || !t->isTileReady())
- return false;
- }
- }
- } else { // SwapWhateveryIsReady
- 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;
- }
+ 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;
}
}
swaps++;
}
- XLOG("%p %s swapped %d textures, returning true",
- this, (swap == SwapWholePage) ? "whole page" : "greedy swap", swaps);
+ XLOG("%p greedy swapped %d textures, returning true", this, swaps);
return fullSwap;
}
+void TiledPage::prepareForDrawGL(float transparency, const SkIRect& tileBounds)
+{
+ m_willDraw = true;
+ m_transparency = transparency;
+ m_tileBounds = tileBounds;
+}
-void TiledPage::draw(float transparency, const SkIRect& tileBounds)
+void TiledPage::drawGL()
{
- if (!m_glWebViewState)
+ if (!m_glWebViewState || m_transparency == 0 || !m_willDraw)
return;
const float tileWidth = TilesManager::tileWidth() * m_invScale;
for (int j = 0; j < m_baseTileSize; j++) {
BaseTile& tile = m_baseTiles[j];
- bool tileInView = tileBounds.contains(tile.x(), tile.y());
+ bool tileInView = m_tileBounds.contains(tile.x(), tile.y());
if (tileInView) {
SkRect rect;
rect.fLeft = tile.x() * tileWidth;
rect.fRight = rect.fLeft + tileWidth;
rect.fBottom = rect.fTop + tileHeight;
- tile.draw(transparency, rect, m_scale);
+ tile.draw(m_transparency, rect, m_scale);
}
TilesManager::instance()->getProfiler()->nextTile(tile, m_invScale, tileInView);
}
+ m_willDraw = false; // don't redraw until re-prepared
}
bool TiledPage::paint(BaseTile* tile, SkCanvas* canvas, unsigned int* pictureUsed)
ExpandedBounds = 0,
VisibleBounds = 1
};
- enum SwapMethod {
- SwapWhateverIsReady = 0,
- SwapWholePage = 1
- };
TiledPage(int id, GLWebViewState* state);
~TiledPage();
// returns true if the page can't draw the entire region (may still be stale)
bool hasMissingContent(const SkIRect& tileBounds);
+ bool isReady(const SkIRect& tileBounds);
+
// swap 'buffers' by swapping each modified texture
- bool swapBuffersIfReady(const SkIRect& tileBounds, float scale, SwapMethod swap);
+ bool swapBuffersIfReady(const SkIRect& tileBounds, float scale);
+ // save the transparency and bounds to be drawn in drawGL()
+ void prepareForDrawGL(float transparency, const SkIRect& tileBounds);
// draw the page on the screen
- void draw(float transparency, const SkIRect& tileBounds);
+ void drawGL();
// TilePainter implementation
// used by individual tiles to generate the bitmap for their tile
void discardTextures();
void updateBaseTileSize();
bool scrollingDown() { return m_scrollingDown; }
- SkIRect* expandedTileBounds() { return &m_expandedTileBounds; }
bool isPrefetchPage() { return m_isPrefetchPage; }
void setIsPrefetchPage(bool isPrefetch) { m_isPrefetchPage = isPrefetch; }
unsigned int m_latestPictureInval;
bool m_prepare;
bool m_scrollingDown;
- SkIRect m_expandedTileBounds;
bool m_isPrefetchPage;
+
+ // info saved in prepare, used in drawGL()
+ bool m_willDraw;
+ SkIRect m_tileBounds;
+ float m_transparency;
};
} // namespace WebCore
#include "PaintedSurface.h"
#include "PaintTileOperation.h"
#include "SkCanvas.h"
+#include "SkPicture.h"
#include <cutils/log.h>
#include <wtf/CurrentTime.h>
namespace WebCore {
-bool TiledTexture::ready() {
+TiledTexture::~TiledTexture()
+{
+ SkSafeUnref(m_paintingPicture);
+#ifdef DEBUG_COUNT
+ ClassTracker::instance()->decrement("TiledTexture");
+#endif
+ removeTiles();
+}
+
+bool TiledTexture::ready()
+{
bool tilesAllReady = true;
bool tilesVisible = false;
for (unsigned int i = 0; i < m_tiles.size(); i++) {
// in order to unblock the zooming process.
// FIXME: have a better system -- maybe keeping the last scale factor
// able to fully render everything
+ XLOG("TT %p, ready %d, visible %d, texturesRemain %d",
+ this, tilesAllReady, tilesVisible,
+ TilesManager::instance()->layerTexturesRemain());
+
return !TilesManager::instance()->layerTexturesRemain()
|| !tilesVisible || tilesAllReady;
}
+void TiledTexture::swapTiles()
+{
+ int swaps = 0;
+ for (unsigned int i = 0; i < m_tiles.size(); i++)
+ if (m_tiles[i]->swapTexturesIfNeeded())
+ swaps++;
+ XLOG("TT %p swapping, swaps = %d", this, swaps);
+}
+
IntRect TiledTexture::computeTilesArea(IntRect& visibleArea, float scale)
{
IntRect computedArea;
ceilf(visibleArea.width() * scale),
ceilf(visibleArea.height() * scale));
+ XLOG("TT %p prepare, scale %f, area %d x %d", this, scale, area.width(), area.height());
+
if (area.width() == 0 && area.height() == 0) {
computedArea.setWidth(0);
computedArea.setHeight(0);
}
void TiledTexture::prepare(GLWebViewState* state, float scale, bool repaint,
- bool startFastSwap, IntRect& area)
+ bool startFastSwap, IntRect& visibleArea)
{
if (!m_surface)
return;
- if (!m_surface->layer())
- return;
-
- m_area = computeTilesArea(area, scale);
+ // first, how many tiles do we need
+ m_area = computeTilesArea(visibleArea, scale);
if (m_area.isEmpty())
return;
m_scale = scale;
- // unlock if tiles all ready
- bool tilesAllReady = ready();
-
- // startFastSwap=true will swap all ready tiles each
- // frame until all visible tiles are up to date
- if (tilesAllReady)
- m_swapWhateverIsReady = false;
- else if (startFastSwap)
- m_swapWhateverIsReady = true;
-
- // swap as appropriate
- for (unsigned int i = 0; i < m_tiles.size(); i++) {
- BaseTile* tile = m_tiles[i];
- if (tilesAllReady || m_swapWhateverIsReady)
- tile->swapTexturesIfNeeded();
- }
-
- if (tilesAllReady) {
- m_updateManager.swap();
- m_dirtyRegion.op(m_updateManager.getPaintingInval(), SkRegion::kUnion_Op);
- XLOG("TT %p swapping, now painting with picture %p"
- this, m_updateManager.getPaintingPicture());
- m_updateManager.clearPaintingInval();
- }
-
// apply dirty region to affected tiles
if (!m_dirtyRegion.isEmpty()) {
for (unsigned int i = 0; i < m_tiles.size(); i++) {
void TiledTexture::update(const SkRegion& invalRegion, SkPicture* picture)
{
- XLOG("TT %p, update manager %p updated with picture %p, region empty %d",
- this, &m_updateManager, picture, invalRegion.isEmpty());
- // attempt to update inval and picture. these may be deferred below instead
- // of used immediately.
- m_updateManager.updateInval(invalRegion);
- m_updateManager.updatePicture(picture);
+ XLOG("TT %p update, current region empty %d, new empty %d, painting picture %p",
+ this, m_dirtyRegion.isEmpty(), invalRegion.isEmpty(), picture);
+ m_dirtyRegion.op(invalRegion, SkRegion::kUnion_Op);
+
+ android::Mutex::Autolock lock(m_paintingPictureSync);
+ SkSafeRef(picture);
+ SkSafeUnref(m_paintingPicture);
+ m_paintingPicture = picture;
}
void TiledTexture::prepareTile(bool repaint, int x, int y)
m_tiles.append(tile);
}
- XLOG("preparing tile %p, painter is this %p", tile, this);
+ XLOG("preparing tile %p at %d, %d, painter is this %p", tile, x, y, this);
tile->setContents(this, x, y, m_scale);
// TODO: move below (which is largely the same for layers / tiled page) into
if (tile->isDirty() || !tile->frontTexture())
tile->reserveTexture();
- LayerAndroid* layer = m_surface->layer();
- if (tile->backTexture() && tile->isDirty() && !tile->isRepaintPending() && layer) {
+ bool hasPicture = m_paintingPicture != 0; // safely read on UI thread, since only UI thread writes
+ if (tile->backTexture() && tile->isDirty() && !tile->isRepaintPending() && hasPicture) {
PaintTileOperation *operation = new PaintTileOperation(tile, m_surface);
TilesManager::instance()->scheduleOperation(operation);
}
bool TiledTexture::draw()
{
+ XLOG("TT %p draw", this);
+
#ifdef DEBUG
TilesManager::instance()->getTilesTracker()->trackLayer();
#endif
rect.fTop = tile->y() * tileHeight;
rect.fRight = rect.fLeft + tileWidth;
rect.fBottom = rect.fTop + tileHeight;
- XLOG(" - [%d], { painter %x vs %x }, tile %x %d,%d at scale %.2f vs %.2f [ready: %d] dirty: %d",
+ XLOG("- [%d], { painter %x vs %x }, tile %x %d,%d at scale %.2f vs %.2f [ready: %d] dirty: %d",
i, this, tile->painter(), tile, tile->x(), tile->y(),
tile->scale(), m_scale, tile->isTileReady(), tile->isDirty());
tile->draw(m_surface->opacity(), rect, m_scale);
bool TiledTexture::paint(BaseTile* tile, SkCanvas* canvas, unsigned int* pictureUsed)
{
- return m_updateManager.paint(tile, canvas, pictureUsed);
+ m_paintingPictureSync.lock();
+ SkPicture* picture = m_paintingPicture;
+ SkSafeRef(picture);
+ m_paintingPictureSync.unlock();
+
+ if (!picture) {
+ XLOG("TT %p COULDNT PAINT, NO PICTURE", this);
+ return false;
+ }
+
+ XLOG("TT %p painting with picture %p", this, picture);
+
+ canvas->drawPicture(*picture);
+
+ SkSafeUnref(picture);
+
+ return true;
}
const TransformationMatrix* TiledTexture::transform()
m_zooming = true;
}
- XLOG("\n*** %x Drawing with scale %.2f, futureScale: %.2f, zooming: %d",
- this, scale, m_futureScale, m_zooming);
+ XLOG("Preparing DTT %p with scale %.2f, m_scale %.2f, futureScale: %.2f, zooming: %d",
+ this, scale, m_scale, m_futureScale, m_zooming);
if (m_scale > 0)
m_frontTexture->prepare(state, m_scale, repaint, startFastSwap, m_preZoomVisibleArea);
m_frontTexture->update(dirtyArea, picture);
}
+void DualTiledTexture::swapTiles()
+{
+ m_backTexture->swapTiles();
+ m_frontTexture->swapTiles();
+}
+
bool DualTiledTexture::owns(BaseTileTexture* texture)
{
bool owns = m_textureA->owns(texture);
#include "SkRegion.h"
#include "TextureOwner.h"
#include "TilePainter.h"
-#include "UpdateManager.h"
class SkCanvas;
namespace WebCore {
-class UpdateManager;
class PaintedSurface;
class TiledTexture : public TilePainter {
public:
TiledTexture(PaintedSurface* surface)
- : m_surface(surface)
+ : m_paintingPicture(0)
+ , m_surface(surface)
, m_prevTileX(0)
, m_prevTileY(0)
, m_scale(1)
ClassTracker::instance()->increment("TiledTexture");
#endif
}
- virtual ~TiledTexture()
- {
-#ifdef DEBUG_COUNT
- ClassTracker::instance()->decrement("TiledTexture");
-#endif
- removeTiles();
- };
+
+ virtual ~TiledTexture();
IntRect computeTilesArea(IntRect& visibleArea, float scale);
void prepare(GLWebViewState* state, float scale, bool repaint,
bool startFastSwap, IntRect& visibleArea);
+ void swapTiles();
bool draw();
void prepareTile(bool repaint, int x, int y);
private:
bool tileIsVisible(BaseTile* tile);
- UpdateManager m_updateManager;
+ // protect m_paintingPicture
+ // update() on UI thread modifies
+ // paint() on texture gen thread reads
+ android::Mutex m_paintingPictureSync;
+ SkPicture* m_paintingPicture;
PaintedSurface* m_surface;
Vector<BaseTile*> m_tiles;
~DualTiledTexture();
void prepare(GLWebViewState* state, float scale, bool repaint,
bool startFastSwap, IntRect& area);
+ void swapTiles();
void swap();
bool draw();
void update(const SkRegion& dirtyArea, SkPicture* picture);
bool owns(BaseTileTexture* texture);
+ bool isReady()
+ {
+ return !m_zooming && m_frontTexture->ready();
+ }
int nbTextures(IntRect& area, float scale)
{
#endif // DEBUG
}
-void TilesManager::swapLayersTextures(LayerAndroid* oldTree, LayerAndroid* newTree)
-{
- if (oldTree)
- oldTree->assignTextureTo(newTree);
-
- if (newTree)
- newTree->createTexture();
-
- GLWebViewState* oldState = 0;
- if (oldTree && !newTree)
- oldState = oldTree->state();
-
- paintedSurfacesCleanup(oldState);
-}
-
void TilesManager::addPaintedSurface(PaintedSurface* surface)
{
m_paintedSurfaces.append(surface);
WTF::Vector<PaintedSurface*> collect;
for (unsigned int i = 0; i < m_paintedSurfaces.size(); i++) {
PaintedSurface* surface = m_paintedSurfaces[i];
- if (!surface->layer() || (state && surface->layer()->state() == state))
+
+ Layer* drawing = surface->drawingLayer();
+ Layer* painting = surface->paintingLayer();
+
+ XLOG("considering PS %p, drawing %p, painting %p", surface, drawing, painting);
+
+ bool drawingMatchesState = state && drawing && (drawing->state() == state);
+ bool paintingMatchesState = state && painting && (painting->state() == state);
+
+ if ((!painting && !drawing) || drawingMatchesState || paintingMatchesState) {
+ XLOG("trying to remove PS %p, painting %p, drawing %p, DMS %d, PMS %d",
+ surface, painting, drawing, drawingMatchesState, paintingMatchesState);
collect.append(surface);
+ }
}
for (unsigned int i = 0; i < collect.size(); i++) {
PaintedSurface* surface = collect[i];
m_paintedSurfaces.remove(m_paintedSurfaces.find(surface));
- surface->removeLayer();
SkSafeUnref(surface);
}
}
static float tileHeight();
static float layerTileWidth();
static float layerTileHeight();
- void paintedSurfacesCleanup(GLWebViewState* state);
+ void paintedSurfacesCleanup(GLWebViewState* state = 0);
void unregisterGLWebViewState(GLWebViewState* state);
void allocateTiles();
return m_drawGLCount;
}
+ int getPaintedSurfaceCount()
+ {
+ return m_paintedSurfaces.size();
+ }
+
private:
TilesManager();
--- /dev/null
+/*
+ * Copyright 2011, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TreeManager.h"
+
+#include "Layer.h"
+#include "BaseLayerAndroid.h"
+#include "TilesManager.h"
+
+#include <cutils/log.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/text/CString.h>
+
+#undef XLOGC
+#define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "TreeManager", __VA_ARGS__)
+
+#ifdef DEBUG
+
+#undef XLOG
+#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "TreeManager", __VA_ARGS__)
+
+#else
+
+#undef XLOG
+#define XLOG(...)
+
+#endif // DEBUG
+
+namespace WebCore {
+
+TreeManager::TreeManager()
+ : m_drawingTree(0)
+ , m_paintingTree(0)
+ , m_queuedTree(0)
+ , m_fastSwapMode(false)
+{
+}
+
+TreeManager::~TreeManager()
+{
+ clearTrees();
+}
+
+// the painting tree has finished painting:
+// discard the drawing tree
+// swap the painting tree in place of the drawing tree
+// and start painting the queued tree
+void TreeManager::swap()
+{
+ // swap can't be called unless painting just finished
+ ASSERT(m_paintingTree);
+
+ android::Mutex::Autolock lock(m_paintSwapLock);
+
+ XLOG("SWAPPING, D %p, P %p, Q %p", m_drawingTree, m_paintingTree, m_queuedTree);
+
+ // if we have a drawing tree, discard it since the painting tree is done
+ if (m_drawingTree) {
+ XLOG("destroying drawing tree %p", m_drawingTree);
+ m_drawingTree->setIsDrawing(false);
+ SkSafeUnref(m_drawingTree);
+ }
+
+ // painting tree becomes the drawing tree
+ XLOG("drawing tree %p", m_paintingTree);
+ m_paintingTree->setIsDrawing(true);
+
+ if (m_queuedTree) {
+ // start painting with the queued tree
+ XLOG("now painting tree %p", m_queuedTree);
+ m_queuedTree->setIsPainting(m_paintingTree);
+ }
+ m_drawingTree = m_paintingTree;
+ m_paintingTree = m_queuedTree;
+ m_queuedTree = 0;
+
+ TilesManager::instance()->paintedSurfacesCleanup();
+
+ XLOG("SWAPPING COMPLETE, D %p, P %p, Q %p", m_drawingTree, m_paintingTree, m_queuedTree);
+}
+
+// clear all of the content in the three trees held by the tree manager
+void TreeManager::clearTrees()
+{
+ // remove painted surfaces from any tree in this view, and set trees as no
+ // longer drawing, to clear ptrs from surfaces to layers
+ GLWebViewState* oldState = 0;
+ if (m_drawingTree) {
+ oldState = m_drawingTree->state();
+ m_drawingTree->setIsDrawing(false);
+ }
+ if (m_paintingTree) {
+ oldState = m_paintingTree->state();
+ m_paintingTree->setIsDrawing(false);
+ }
+
+ XLOG("TreeManager %p removing PS from state %p", this, oldState);
+ TilesManager::instance()->paintedSurfacesCleanup(oldState);
+
+ SkSafeUnref(m_drawingTree);
+ m_drawingTree = 0;
+ SkSafeUnref(m_paintingTree);
+ m_paintingTree = 0;
+ SkSafeUnref(m_queuedTree);
+ m_queuedTree = 0;
+}
+
+// a new layer tree has arrived, queue it if we're painting something already,
+// or start painting it if we aren't
+void TreeManager::updateWithTree(Layer* newTree, bool brandNew)
+{
+
+ // can't have a queued tree unless have a painting tree too
+ ASSERT(m_paintingTree || !m_queuedTree);
+
+ SkSafeRef(newTree);
+
+ android::Mutex::Autolock lock(m_paintSwapLock);
+
+ if (!newTree || brandNew) {
+ clearTrees();
+ if (brandNew) {
+ m_animationOffset = 0;
+ m_isAnimating = false;
+ m_lastFrameTime = WTF::currentTime();
+
+ m_paintingTree = newTree;
+ m_paintingTree->setIsPainting(m_drawingTree);
+ }
+ return;
+ }
+
+ if (m_queuedTree || m_paintingTree) {
+ // currently painting, so defer this new tree
+ if (m_queuedTree) {
+ // have a queued tree, copy over invals so the regions are
+ // eventually repainted
+ m_queuedTree->mergeInvalsInto(newTree);
+ }
+ SkSafeUnref(m_queuedTree);
+ m_queuedTree = newTree;
+ return;
+ }
+
+ // don't have painting tree, paint this one!
+ m_paintingTree = newTree;
+ m_paintingTree->setIsPainting(m_drawingTree);
+}
+
+bool TreeManager::drawGL(double currentTime, IntRect& viewRect,
+ SkRect& visibleRect, float scale,
+ bool enterFastSwapMode, bool* buffersSwappedPtr,
+ TexturesResult* texturesResultPtr)
+{
+ m_fastSwapMode |= enterFastSwapMode;
+
+ XLOG("drawGL, D %p, P %p, Q %p, fastSwap %d",
+ m_drawingTree, m_paintingTree, m_queuedTree, m_fastSwapMode);
+
+ bool ret = false;
+ bool didTreeSwap = false;
+ if (m_paintingTree) {
+ ret |= m_paintingTree->prepare(currentTime, viewRect,
+ visibleRect, scale);
+
+ if (/*!m_fastSwapMode && */ m_paintingTree->isReady()) {
+ XLOG("have painting tree %p ready, swapping!", m_paintingTree);
+ didTreeSwap = true;
+ swap();
+ if (buffersSwappedPtr)
+ *buffersSwappedPtr = true;
+ }
+ } else if (m_drawingTree) {
+ XLOG("preparing drawing tree %p", m_drawingTree);
+ ret |= m_drawingTree->prepare(currentTime, viewRect,
+ visibleRect, scale);
+ }
+
+ if (!m_isAnimating) {
+ m_animationOffset += currentTime - m_lastFrameTime;
+#ifdef ANIM_DEBUG
+ XLOGC("adding to %f", m_animationOffset);
+#endif
+ }
+
+ if (m_drawingTree) {
+ bool drawingReady = didTreeSwap || m_drawingTree->isReady();
+
+ if (didTreeSwap || m_fastSwapMode)
+ m_drawingTree->swapTiles();
+
+ if (drawingReady) {
+ // exit fast swap mode, as content is up to date
+ m_fastSwapMode = false;
+ } else {
+ // drawing isn't ready, must redraw
+ ret = true;
+ }
+
+ if (m_drawingTree->countChildren()) {
+#ifdef ANIM_DEBUG
+ XLOGC("drawing tree %p with animation time offset of %f, locked %d",
+ m_drawingTree, m_animationOffset, m_isAnimating);
+#endif
+ LayerAndroid* laTree = static_cast<LayerAndroid*>(m_drawingTree->getChild(0));
+ laTree->computeTexturesAmount(texturesResultPtr);
+ m_isAnimating = laTree->evaluateAnimations(currentTime - m_animationOffset);
+ if (!m_isAnimating)
+ m_animationOffset = 0;
+ ret |= m_isAnimating;
+ } else if (!m_paintingTree) {
+ m_animationOffset = 0;
+ m_isAnimating = false;
+ }
+ XLOG("drawing tree %p", m_drawingTree);
+ ret |= m_drawingTree->drawGL(viewRect, visibleRect, scale);
+ } else if (m_paintingTree && m_paintingTree->state()) {
+ // Dont have a drawing tree, draw white background
+ Color defaultBackground = Color::white;
+ m_paintingTree->state()->drawBackground(defaultBackground);
+ }
+
+ m_lastFrameTime = currentTime;
+
+ if (m_paintingTree) {
+ XLOG("still have painting tree %p", m_paintingTree);
+ return true;
+ }
+
+ return ret;
+}
+
+int TreeManager::getTotalPaintedSurfaceCount()
+{
+ return TilesManager::instance()->getPaintedSurfaceCount();
+}
+
+// draw for base tile - called on TextureGeneration thread
+void TreeManager::drawCanvas(SkCanvas* canvas, bool drawLayers)
+{
+ BaseLayerAndroid* paintingTree = 0;
+ m_paintSwapLock.lock();
+ if (m_paintingTree)
+ paintingTree = static_cast<BaseLayerAndroid*>(m_paintingTree);
+ else
+ paintingTree = static_cast<BaseLayerAndroid*>(m_drawingTree);
+ SkSafeRef(paintingTree);
+ m_paintSwapLock.unlock();
+
+ if (!paintingTree)
+ return;
+
+
+ paintingTree->drawCanvas(canvas);
+
+ if (drawLayers && paintingTree->countChildren()) {
+ // draw the layers onto the canvas as well
+ Layer* layers = paintingTree->getChild(0);
+ static_cast<LayerAndroid*>(layers)->drawCanvas(canvas);
+ }
+
+ SkSafeUnref(paintingTree);
+}
+
+int TreeManager::baseContentWidth()
+{
+ if (m_paintingTree) {
+ return static_cast<BaseLayerAndroid*>(m_paintingTree)->content()->width();
+ } else if (m_drawingTree) {
+ return static_cast<BaseLayerAndroid*>(m_drawingTree)->content()->width();
+ }
+ return 0;
+}
+
+int TreeManager::baseContentHeight()
+{
+ if (m_paintingTree) {
+ return static_cast<BaseLayerAndroid*>(m_paintingTree)->content()->height();
+ } else if (m_drawingTree) {
+ return static_cast<BaseLayerAndroid*>(m_drawingTree)->content()->height();
+ }
+ return 0;
+}
+
+} // namespace WebCore
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef UpdateManager_h
-#define UpdateManager_h
+#ifndef TreeManager_h
+#define TreeManager_h
+#include "TestExport.h"
+#include <utils/threads.h>
+
+class Layer;
+class SkRect;
class SkCanvas;
-class SkRegion;
namespace WebCore {
-class BaseTile;
-
-// UpdateManager Architecture
+class IntRect;
+class TexturesResult;
-// The UpdateManager is used to defer updates and invalidations to a layer,
-// so that the layer can finish painting one version completely without being
-// interrupted by new invals/content
-
-class UpdateManager {
+class TEST_EXPORT TreeManager {
public:
- UpdateManager();
-
- ~UpdateManager();
+ TreeManager();
- // swap deferred members in place of painting members
- void swap();
-
- void updateInval(const SkRegion& invalRegion);
+ ~TreeManager();
- void updatePicture(SkPicture* picture);
+ void updateWithTree(Layer* tree, bool brandNew);
- bool paint(BaseTile* tile, SkCanvas* canvas, unsigned int* pictureUsed);
+ bool drawGL(double currentTime, IntRect& viewRect,
+ SkRect& visibleRect, float scale,
+ bool enterFastSwapMode, bool* buffersSwappedPtr,
+ TexturesResult* texturesResultPtr);
- void clearPaintingInval();
+ void drawCanvas(SkCanvas* canvas, bool drawLayers);
- const SkRegion& getPaintingInval() {
- return m_paintingInval;
- }
+ // used in debugging (to avoid exporting TilesManager symbols)
+ static int getTotalPaintedSurfaceCount();
- SkPicture* getPaintingPicture() {
- // NOTE: only modified on UI thread, so UI thread access doesn't need mutex
- return m_paintingPicture;
- }
+ int baseContentWidth();
+ int baseContentHeight();
private:
- // protect m_paintingPicture
- // swap() on UI thread modifies
- // paint() on texture gen thread reads
- android::Mutex m_paintingPictureSync;
- SkPicture* m_paintingPicture;
+ void swap();
+ void clearTrees();
+
+ android::Mutex m_paintSwapLock;
- // inval region to be redrawn with the current paintingPicture
- SkRegion m_paintingInval;
+ Layer* m_drawingTree;
+ Layer* m_paintingTree;
+ Layer* m_queuedTree;
- // most recently received picture, moved into painting on swap()
- SkPicture* m_deferredPicture;
+ bool m_fastSwapMode;
- // all invals since last swap(), merged with painting inval on swap()
- SkRegion m_deferredInval;
+ double m_animationOffset;
+ double m_lastFrameTime;
+ bool m_isAnimating;
};
} // namespace WebCore
-#endif //#define UpdateManager_h
+#endif //#define TreeManager_h
+++ /dev/null
-/*
- * Copyright 2011, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "PaintedSurface.h"
-
-#include "LayerAndroid.h"
-#include "TilesManager.h"
-#include "SkCanvas.h"
-#include "SkPicture.h"
-
-#include <cutils/log.h>
-#include <wtf/CurrentTime.h>
-#include <wtf/text/CString.h>
-
-#undef XLOGC
-#define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "UpdateManager", __VA_ARGS__)
-
-#ifdef DEBUG
-
-#undef XLOG
-#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "UpdateManager", __VA_ARGS__)
-
-#else
-
-#undef XLOG
-#define XLOG(...)
-
-#endif // DEBUG
-
-namespace WebCore {
-
-UpdateManager::UpdateManager()
- : m_paintingPicture(0)
- , m_deferredPicture(0)
-{
-}
-
-UpdateManager::~UpdateManager()
-{
- SkSafeUnref(m_paintingPicture);
- SkSafeUnref(m_deferredPicture);
-}
-
-void UpdateManager::swap()
-{
- m_paintingInval.op(m_deferredInval, SkRegion::kUnion_Op);
- m_deferredInval.setEmpty();
-
- android::Mutex::Autolock lock(m_paintingPictureSync);
- if (m_deferredPicture) {
- XLOG("unlock of updatemanager %p, was painting %p, now painting %p",
- this, m_paintingPicture, m_deferredPicture);
- SkSafeUnref(m_paintingPicture);
- m_paintingPicture = m_deferredPicture;
- m_deferredPicture = 0;
- }
-}
-
-void UpdateManager::updateInval(const SkRegion& invalRegion)
-{
- m_deferredInval.op(invalRegion, SkRegion::kUnion_Op);
-}
-
-void UpdateManager::updatePicture(SkPicture* picture)
-{
- SkSafeRef(picture);
- SkSafeUnref(m_deferredPicture);
- m_deferredPicture = picture;
-}
-
-bool UpdateManager::paint(BaseTile* tile, SkCanvas* canvas, unsigned int* pictureUsed)
-{
- m_paintingPictureSync.lock();
- SkPicture* picture = m_paintingPicture;
- SkSafeRef(picture);
- m_paintingPictureSync.unlock();
-
- XLOG("UpdateManager %p painting with picture %p", this, picture);
-
- if (!picture)
- return false;
-
- canvas->drawPicture(*picture);
-
- // TODO: visualization layer diagonals
-
- SkSafeUnref(picture);
- return true;
-}
-
-
-void UpdateManager::clearPaintingInval()
-{
- m_paintingInval.setEmpty();
-}
-
-} // namespace WebCore
return texture;
}
-bool VideoLayerAndroid::drawGL(GLWebViewState* glWebViewState, SkMatrix& matrix)
+bool VideoLayerAndroid::drawGL()
{
// Lazily allocated the textures.
if (!m_createdTexture) {
}
}
- return drawChildrenGL(glWebViewState, matrix);
+ return drawChildrenGL();
}
}
virtual LayerAndroid* copy() const { return new VideoLayerAndroid(*this); }
// The following 3 functions are called in UI thread only.
- virtual bool drawGL(GLWebViewState*, SkMatrix& matrix);
+ virtual bool drawGL();
void setSurfaceTexture(sp<SurfaceTexture> texture, int textureName, PlayerState playerState);
GLuint createBackgroundTexture();
GLuint createSpinnerOuterTexture();
--- /dev/null
+# Build the unit tests.
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# Build the unit tests.
+test_src_files := \
+ TreeManager_test.cpp
+
+shared_libraries := \
+ libcutils \
+ libwebcore \
+ libskia \
+ libstlport
+
+static_libraries := \
+ libgtest \
+ libgtest_main
+
+c_includes := \
+ bionic \
+ bionic/libstdc++/include \
+ external/gtest/include \
+ external/stlport/stlport \
+ external/skia/include/core \
+ external/icu4c/common \
+ $(LOCAL_PATH)/../../JavaScriptCore \
+ $(LOCAL_PATH)/../../JavaScriptCore/wtf \
+ $(LOCAL_PATH)/.. \
+ $(LOCAL_PATH)/../platform/graphics \
+ $(LOCAL_PATH)/../platform/graphics/transforms \
+ $(LOCAL_PATH)/../platform/graphics/android
+
+ # external/webkit/Source/WebCore/platform/graphics/android
+
+module_tags := eng tests
+
+$(foreach file,$(test_src_files), \
+ $(eval include $(CLEAR_VARS)) \
+ $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
+ $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
+ $(eval LOCAL_C_INCLUDES := $(c_includes)) \
+ $(eval LOCAL_SRC_FILES := $(file)) \
+ $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
+ $(eval LOCAL_MODULE_TAGS := $(module_tags)) \
+ $(eval include $(BUILD_EXECUTABLE)) \
+)
+
+# Build the manual test programs.
+include $(call all-makefiles-under, $(LOCAL_PATH))
--- /dev/null
+/*
+ * Copyright 2011, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <gtest/gtest.h>
+
+#include "SkRefCnt.h"
+#include "TransformationMatrix.h"
+#include "IntRect.h"
+#include "Layer.h"
+#include "LayerAndroid.h"
+#include "TreeManager.h"
+#include "SkPicture.h"
+
+#include <cutils/log.h>
+#include <wtf/text/CString.h>
+#define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "TreeManager_test", __VA_ARGS__)
+
+namespace WebCore {
+
+// Used for testing simple cases for tree painting, drawing, swapping
+class TestLayer : public Layer {
+public:
+ TestLayer()
+ : m_isDrawing(false)
+ , m_isPainting(false)
+ , m_isDonePainting(false)
+ , m_drawCount(0)
+ {}
+
+ bool m_isDrawing;
+ bool m_isPainting;
+ bool m_isDonePainting;
+ double m_drawCount;
+
+ bool drawGL(WebCore::IntRect& viewRect, SkRect& visibleRect, float scale) {
+ m_drawCount++;
+ return false;
+ }
+
+ bool isReady() {
+ return m_isDonePainting;
+ }
+
+ void setIsDrawing(bool isDrawing) {
+ m_isDrawing = isDrawing;
+ if (isDrawing)
+ m_isPainting = false;
+ }
+
+ void setIsPainting(Layer* drawingTree) {
+ m_isPainting = true;
+ m_isDonePainting = false;
+ setIsDrawing(false);
+ }
+};
+
+// Used for testing complex trees, and painted surfaces
+class TestLayerAndroid : public LayerAndroid {
+public:
+ TestLayerAndroid(SkPicture* picture) : LayerAndroid(picture)
+ , m_isDonePainting(false)
+ , m_drawCount(0)
+ {}
+
+ TestLayerAndroid(const TestLayerAndroid& testLayer) : LayerAndroid(testLayer)
+ , m_isDonePainting(testLayer.m_isDonePainting)
+ , m_drawCount(testLayer.m_drawCount)
+ {
+ XLOGC("copying TLA %p as %p", &testLayer, this);
+ }
+
+ bool m_isDonePainting;
+ double m_drawCount;
+
+ bool drawGL(WebCore::IntRect& viewRect, SkRect& visibleRect, float scale) {
+ m_drawCount++;
+ return false;
+ }
+
+ bool isReady() {
+ return m_isDonePainting;
+ }
+
+ LayerAndroid* copy() const { return new TestLayerAndroid(*this); }
+};
+
+class TreeManagerTest : public testing::Test {
+protected:
+ IntRect m_iRect;
+ SkRect m_sRect;
+ double m_scale;
+
+ void allocLayerWithPicture(bool useTestLayer, LayerAndroid** layerHandle, SkPicture** pictureHandle) {
+ SkPicture* p = new SkPicture();
+ p->beginRecording(16,16, 0);
+ p->endRecording();
+
+ LayerAndroid* l;
+ if (useTestLayer)
+ l = new TestLayerAndroid(p);
+ else
+ l = new LayerAndroid(p);
+ l->setSize(16, 16);
+ SkSafeUnref(p); // layer takes sole ownership of picture
+
+ if (layerHandle)
+ *layerHandle = l;
+ if (pictureHandle)
+ *pictureHandle = p;
+ }
+
+ bool drawGL(TreeManager& manager, bool* swappedPtr) {
+ // call draw gl here in one place, so that when its parameters change,
+ // the tests only have to be updated in one place
+ return manager.drawGL(0, m_iRect, m_sRect, m_scale, false, swappedPtr, 0);
+ }
+
+ virtual void SetUp() {
+ m_iRect = IntRect(0, 0, 1, 1);
+ m_sRect = SkRect::MakeWH(1, 1);
+ m_scale = 1.0;
+ }
+ virtual void TearDown() { }
+};
+
+TEST_F(TreeManagerTest, EmptyTree_DoesntRedraw) {
+ TreeManager manager;
+
+ drawGL(manager, false);
+}
+
+TEST_F(TreeManagerTest, OneLayerTree_SingleTree_SwapCheck) {
+ TreeManager manager;
+ TestLayer layer;
+
+ // initialize with tree, should be painting
+ manager.updateWithTree(&layer, true);
+
+ ASSERT_TRUE(layer.m_isPainting);
+ ASSERT_FALSE(layer.m_isDrawing);
+
+ // should not call swap, and return true since content isn't done
+ for (int i = 1; i < 6; i++) {
+ bool swapped = false;
+ ASSERT_TRUE(drawGL(manager, &swapped));
+ ASSERT_FALSE(swapped);
+ ASSERT_EQ(layer.m_drawCount, 0);
+ }
+
+ layer.m_isDonePainting = true;
+
+ // swap content, should return false since no new picture
+ bool swapped = false;
+ ASSERT_FALSE(drawGL(manager, &swapped));
+ ASSERT_TRUE(swapped);
+ ASSERT_EQ(layer.m_drawCount, 1); // verify layer drawn
+}
+
+TEST_F(TreeManagerTest, OneLayerTree_SingleTree_RefCountCorrectly) {
+ TreeManager manager;
+ TestLayer* layer = new TestLayer();
+ ASSERT_EQ(layer->getRefCnt(), 1);
+
+ // initialize with tree, should be painting
+ manager.updateWithTree(layer, true);
+ ASSERT_EQ(layer->getRefCnt(), 2);
+
+ layer->m_isDonePainting = true;
+ ASSERT_FALSE(drawGL(manager, 0));
+
+ // should be drawing
+ ASSERT_EQ(layer->getRefCnt(), 2);
+
+ manager.updateWithTree(0, false);
+
+ // layer should be removed
+ ASSERT_EQ(layer->getRefCnt(), 1);
+ SkSafeUnref(layer);
+}
+
+TEST_F(TreeManagerTest, OneLayerTree_TwoTreeFlush_PaintDrawRefCheck) {
+ TreeManager manager;
+ TestLayer* firstLayer = new TestLayer();
+ TestLayer* secondLayer = new TestLayer();
+ ASSERT_EQ(firstLayer->getRefCnt(), 1);
+ ASSERT_EQ(secondLayer->getRefCnt(), 1);
+
+ ///// ENQUEUE 2 TREES
+
+ // first starts painting
+ manager.updateWithTree(firstLayer, true);
+ ASSERT_TRUE(firstLayer->m_isPainting);
+ ASSERT_FALSE(firstLayer->m_isDrawing);
+
+ // second is queued
+ manager.updateWithTree(secondLayer, false);
+ ASSERT_FALSE(secondLayer->m_isPainting);
+ ASSERT_FALSE(secondLayer->m_isDrawing);
+
+ // nothing changes
+ ASSERT_TRUE(drawGL(manager, 0));
+
+ ////////// FIRST FINISHES PAINTING, SWAP THE TREES
+
+ firstLayer->m_isDonePainting = true;
+ bool swapped = false;
+ ASSERT_TRUE(drawGL(manager, &swapped));
+ ASSERT_TRUE(swapped);
+
+ // first is drawing
+ ASSERT_EQ(firstLayer->m_drawCount, 1);
+ ASSERT_FALSE(firstLayer->m_isPainting);
+ ASSERT_TRUE(firstLayer->m_isDrawing);
+ ASSERT_EQ(firstLayer->getRefCnt(), 2);
+
+ // second is painting (and hasn't drawn)
+ ASSERT_EQ(secondLayer->m_drawCount, 0);
+ ASSERT_TRUE(secondLayer->m_isPainting);
+ ASSERT_FALSE(secondLayer->m_isDrawing);
+ ASSERT_EQ(secondLayer->getRefCnt(), 2);
+
+ ////////// SECOND FINISHES PAINTING, SWAP AGAIN
+
+ secondLayer->m_isDonePainting = true;
+
+ // draw again, swap, first should be deleted
+ swapped = false;
+ ASSERT_FALSE(drawGL(manager, &swapped)); // no painting layer
+ ASSERT_TRUE(swapped);
+
+ // first layer gone!
+ ASSERT_EQ(firstLayer->getRefCnt(), 1);
+ SkSafeUnref(firstLayer);
+
+ // second is drawing
+ ASSERT_EQ(secondLayer->m_drawCount, 1);
+ ASSERT_FALSE(secondLayer->m_isPainting);
+ ASSERT_TRUE(secondLayer->m_isDrawing);
+ ASSERT_EQ(secondLayer->getRefCnt(), 2);
+
+ ////////// INSERT NULL, BOTH TREES NOW REMOVED
+
+ // insert null tree, which should deref secondLayer immediately
+ manager.updateWithTree(0, false);
+ ASSERT_EQ(secondLayer->getRefCnt(), 1);
+ SkSafeUnref(secondLayer);
+
+ // nothing to draw or swap
+ swapped = false;
+ ASSERT_FALSE(drawGL(manager, &swapped));
+ ASSERT_FALSE(swapped);
+}
+
+TEST_F(TreeManagerTest, LayerAndroidTree_PictureRefCount) {
+ RenderLayer* renderLayer = 0;
+ LayerAndroid* l;
+ SkPicture* p;
+ allocLayerWithPicture(false, &l, &p);
+ ASSERT_TRUE(l->needsTexture());
+ SkSafeRef(p); // ref picture locally so it exists after layer (so we can see
+ // layer derefs it)
+
+ ASSERT_EQ(l->getRefCnt(), 1);
+ ASSERT_EQ(p->getRefCnt(), 2);
+ SkSafeUnref(l);
+
+ ASSERT_EQ(p->getRefCnt(), 1);
+ SkSafeUnref(p);
+}
+
+TEST_F(TreeManagerTest, LayerAndroidTree_PaintTreeWithPictures) {
+ XLOGC("STARTING PAINT TEST");
+
+ TreeManager manager;
+ RenderLayer* renderLayer = 0;
+ LayerAndroid root(renderLayer);
+ LayerAndroid* noPaintChild = new LayerAndroid(renderLayer);
+ root.addChild(noPaintChild);
+
+ root.showLayer(0);
+
+ ASSERT_EQ(noPaintChild->getRefCnt(), 2);
+
+
+ LayerAndroid* copy = new LayerAndroid(root);
+ copy->showLayer(0);
+ manager.updateWithTree(copy, true);
+
+
+ // no painting layer, should swap immediately
+ bool swapped = false;
+ ASSERT_FALSE(drawGL(manager, &swapped));
+ ASSERT_TRUE(swapped);
+
+
+ ////////// add 2 painting layers, push new tree copy into tree manager
+
+ LayerAndroid* paintChildA;
+ allocLayerWithPicture(true, &paintChildA, 0);
+ noPaintChild->addChild(paintChildA);
+ ASSERT_TRUE(paintChildA->needsTexture());
+
+ LayerAndroid* paintChildB;
+ allocLayerWithPicture(true, &paintChildB, 0);
+ noPaintChild->addChild(paintChildB);
+ ASSERT_TRUE(paintChildB->needsTexture());
+
+ LayerAndroid* copy1 = new LayerAndroid(root);
+ copy1->showLayer(0);
+ manager.updateWithTree(copy1, false);
+
+ swapped = false;
+ ASSERT_TRUE(drawGL(manager, &swapped));
+ ASSERT_FALSE(swapped); // painting layers not ready
+
+ ASSERT_EQ(TreeManager::getTotalPaintedSurfaceCount(), 2);
+
+ ////////// remove painting layer, add new painting layer, push new tree copy into tree manager
+
+ LayerAndroid* paintChildC;
+ allocLayerWithPicture(true, &paintChildC, 0);
+ noPaintChild->addChild(paintChildC);
+ ASSERT_TRUE(paintChildC->needsTexture());
+
+ paintChildB->detachFromParent();
+ ASSERT_EQ(paintChildB->getRefCnt(), 1);
+ SkSafeUnref(paintChildB);
+
+ LayerAndroid* copy2 = new LayerAndroid(root);
+ copy2->showLayer(0);
+ manager.updateWithTree(copy2, false);
+
+ swapped = false;
+ ASSERT_TRUE(drawGL(manager, &swapped));
+ ASSERT_FALSE(swapped); // painting layers not ready
+
+
+ ////////// swap layers
+
+ static_cast<TestLayerAndroid*>(copy1->getChild(0)->getChild(0))->m_isDonePainting = true;
+ static_cast<TestLayerAndroid*>(copy1->getChild(0)->getChild(1))->m_isDonePainting = true;
+
+ XLOGC("painting should be %p, queued %p", copy1, copy2);
+ swapped = false;
+ ASSERT_TRUE(drawGL(manager, &swapped));
+ ASSERT_TRUE(swapped); // paint complete, new layer tree to paint
+ XLOGC("drawing should be %p, painting %p", copy1, copy2);
+
+
+ ASSERT_EQ(TreeManager::getTotalPaintedSurfaceCount(), 3);
+
+
+ ////////// swap layers again
+
+ static_cast<TestLayerAndroid*>(copy2->getChild(0)->getChild(0))->m_isDonePainting = true;
+ static_cast<TestLayerAndroid*>(copy2->getChild(0)->getChild(1))->m_isDonePainting = true;
+
+ swapped = false;
+ ASSERT_FALSE(drawGL(manager, &swapped));
+ ASSERT_TRUE(swapped); // paint complete, no new layer tree to paint
+
+ ASSERT_EQ(TreeManager::getTotalPaintedSurfaceCount(), 2);
+
+ ////////// remove all painting layers
+
+ paintChildA->detachFromParent();
+ SkSafeUnref(paintChildA);
+ paintChildC->detachFromParent();
+ SkSafeUnref(paintChildC);
+
+
+ copy = new LayerAndroid(root);
+ copy->showLayer(0);
+ manager.updateWithTree(copy, false);
+
+ swapped = false;
+ ASSERT_FALSE(drawGL(manager, &swapped));
+ ASSERT_TRUE(swapped); // paint complete, no new layer tree to paint
+
+ ASSERT_EQ(TreeManager::getTotalPaintedSurfaceCount(), 0);
+}
+
+} // namespace WebCore