/* * 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 #include "SkRefCnt.h" #include "TransformationMatrix.h" #include "IntRect.h" #include "Layer.h" #include "LayerAndroid.h" #include "TreeManager.h" #include "SkPicture.h" #include #include #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(copy1->getChild(0)->getChild(0))->m_isDonePainting = true; static_cast(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(copy2->getChild(0)->getChild(0))->m_isDonePainting = true; static_cast(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