| /* |
| * 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 |