Home | History | Annotate | Download | only in unit
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <VectorDrawable.h>
     18 #include <gtest/gtest.h>
     19 
     20 #include "AnimationContext.h"
     21 #include "DamageAccumulator.h"
     22 #include "IContextFactory.h"
     23 #include "pipeline/skia/GLFunctorDrawable.h"
     24 #include "pipeline/skia/SkiaDisplayList.h"
     25 #include "renderthread/CanvasContext.h"
     26 #include "tests/common/TestContext.h"
     27 #include "tests/common/TestUtils.h"
     28 
     29 using namespace android;
     30 using namespace android::uirenderer;
     31 using namespace android::uirenderer::renderthread;
     32 using namespace android::uirenderer::skiapipeline;
     33 
     34 TEST(SkiaDisplayList, create) {
     35     SkiaDisplayList skiaDL;
     36     ASSERT_TRUE(skiaDL.isEmpty());
     37     ASSERT_FALSE(skiaDL.mProjectionReceiver);
     38 }
     39 
     40 TEST(SkiaDisplayList, reset) {
     41     std::unique_ptr<SkiaDisplayList> skiaDL;
     42     {
     43         SkiaRecordingCanvas canvas{nullptr, 1, 1};
     44         canvas.drawColor(0, SkBlendMode::kSrc);
     45         skiaDL.reset(canvas.finishRecording());
     46     }
     47 
     48     SkCanvas dummyCanvas;
     49     RenderNodeDrawable drawable(nullptr, &dummyCanvas);
     50     skiaDL->mChildNodes.emplace_back(nullptr, &dummyCanvas);
     51     GLFunctorDrawable functorDrawable(nullptr, nullptr, &dummyCanvas);
     52     skiaDL->mChildFunctors.push_back(&functorDrawable);
     53     skiaDL->mMutableImages.push_back(nullptr);
     54     skiaDL->appendVD(nullptr);
     55     skiaDL->mProjectionReceiver = &drawable;
     56 
     57     ASSERT_FALSE(skiaDL->mChildNodes.empty());
     58     ASSERT_FALSE(skiaDL->mChildFunctors.empty());
     59     ASSERT_FALSE(skiaDL->mMutableImages.empty());
     60     ASSERT_TRUE(skiaDL->hasVectorDrawables());
     61     ASSERT_FALSE(skiaDL->isEmpty());
     62     ASSERT_TRUE(skiaDL->mProjectionReceiver);
     63 
     64     skiaDL->reset();
     65 
     66     ASSERT_TRUE(skiaDL->mChildNodes.empty());
     67     ASSERT_TRUE(skiaDL->mChildFunctors.empty());
     68     ASSERT_TRUE(skiaDL->mMutableImages.empty());
     69     ASSERT_FALSE(skiaDL->hasVectorDrawables());
     70     ASSERT_TRUE(skiaDL->isEmpty());
     71     ASSERT_FALSE(skiaDL->mProjectionReceiver);
     72 }
     73 
     74 TEST(SkiaDisplayList, reuseDisplayList) {
     75     sp<RenderNode> renderNode = new RenderNode();
     76     std::unique_ptr<SkiaDisplayList> availableList;
     77 
     78     // no list has been attached so it should return a nullptr
     79     availableList = renderNode->detachAvailableList();
     80     ASSERT_EQ(availableList.get(), nullptr);
     81 
     82     // attach a displayList for reuse
     83     SkiaDisplayList skiaDL;
     84     ASSERT_TRUE(skiaDL.reuseDisplayList(renderNode.get(), nullptr));
     85 
     86     // detach the list that you just attempted to reuse
     87     availableList = renderNode->detachAvailableList();
     88     ASSERT_EQ(availableList.get(), &skiaDL);
     89     availableList.release();  // prevents an invalid free since our DL is stack allocated
     90 
     91     // after detaching there should return no available list
     92     availableList = renderNode->detachAvailableList();
     93     ASSERT_EQ(availableList.get(), nullptr);
     94 }
     95 
     96 TEST(SkiaDisplayList, syncContexts) {
     97     SkiaDisplayList skiaDL;
     98 
     99     SkCanvas dummyCanvas;
    100     TestUtils::MockFunctor functor;
    101     GLFunctorDrawable functorDrawable(&functor, nullptr, &dummyCanvas);
    102     skiaDL.mChildFunctors.push_back(&functorDrawable);
    103 
    104     int functor2 = WebViewFunctor_create(
    105             nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES);
    106     auto& counts = TestUtils::countsForFunctor(functor2);
    107     skiaDL.mChildFunctors.push_back(
    108             skiaDL.allocateDrawable<GLFunctorDrawable>(functor2, &dummyCanvas));
    109     WebViewFunctor_release(functor2);
    110 
    111     SkRect bounds = SkRect::MakeWH(200, 200);
    112     VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
    113     vectorDrawable.mutateStagingProperties()->setBounds(bounds);
    114     skiaDL.appendVD(&vectorDrawable);
    115 
    116     // ensure that the functor and vectorDrawable are properly synced
    117     TestUtils::runOnRenderThread([&](auto&) {
    118         skiaDL.syncContents(WebViewSyncData{
    119                 .applyForceDark = false,
    120         });
    121     });
    122 
    123     EXPECT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync);
    124     EXPECT_EQ(counts.sync, 1);
    125     EXPECT_EQ(counts.destroyed, 0);
    126     EXPECT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
    127 
    128     skiaDL.reset();
    129     TestUtils::runOnRenderThread([](auto&) {
    130         // Fence
    131     });
    132     EXPECT_EQ(counts.destroyed, 1);
    133 }
    134 
    135 class ContextFactory : public IContextFactory {
    136 public:
    137     virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
    138         return new AnimationContext(clock);
    139     }
    140 };
    141 
    142 RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) {
    143     auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
    144     ContextFactory contextFactory;
    145     std::unique_ptr<CanvasContext> canvasContext(
    146             CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
    147     TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
    148     DamageAccumulator damageAccumulator;
    149     info.damageAccumulator = &damageAccumulator;
    150 
    151     SkiaDisplayList skiaDL;
    152 
    153     // The VectorDrawableRoot needs to have bounds on screen (and therefore not
    154     // empty) in order to have PropertyChangeWillBeConsumed set.
    155     const auto bounds = SkRect::MakeIWH(100, 100);
    156 
    157     // prepare with a clean VD
    158     VectorDrawableRoot cleanVD(new VectorDrawable::Group());
    159     cleanVD.mutateProperties()->setBounds(bounds);
    160     skiaDL.appendVD(&cleanVD);
    161     cleanVD.getBitmapUpdateIfDirty();  // this clears the dirty bit
    162 
    163     ASSERT_FALSE(cleanVD.isDirty());
    164     ASSERT_FALSE(cleanVD.getPropertyChangeWillBeConsumed());
    165     TestUtils::MockTreeObserver observer;
    166     ASSERT_FALSE(skiaDL.prepareListAndChildren(observer, info, false,
    167                                                [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
    168     ASSERT_FALSE(cleanVD.getPropertyChangeWillBeConsumed());
    169 
    170     // prepare again this time adding a dirty VD
    171     VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
    172     dirtyVD.mutateProperties()->setBounds(bounds);
    173     skiaDL.appendVD(&dirtyVD);
    174 
    175     ASSERT_TRUE(dirtyVD.isDirty());
    176     ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
    177     ASSERT_TRUE(skiaDL.prepareListAndChildren(observer, info, false,
    178                                               [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
    179     ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
    180 
    181     // prepare again this time adding a RenderNode and a callback
    182     sp<RenderNode> renderNode = new RenderNode();
    183     TreeInfo* infoPtr = &info;
    184     SkCanvas dummyCanvas;
    185     skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas);
    186     bool hasRun = false;
    187     ASSERT_TRUE(skiaDL.prepareListAndChildren(
    188             observer, info, false,
    189             [&hasRun, renderNode, infoPtr](RenderNode* n, TreeObserver& observer, TreeInfo& i,
    190                                            bool r) {
    191                 hasRun = true;
    192                 ASSERT_EQ(renderNode.get(), n);
    193                 ASSERT_EQ(infoPtr, &i);
    194                 ASSERT_FALSE(r);
    195             }));
    196     ASSERT_TRUE(hasRun);
    197 
    198     canvasContext->destroy();
    199 }
    200 
    201 RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) {
    202     auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
    203     ContextFactory contextFactory;
    204     std::unique_ptr<CanvasContext> canvasContext(
    205             CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
    206 
    207     // Set up a Surface so that we can position the VectorDrawable offscreen.
    208     test::TestContext testContext;
    209     testContext.setRenderOffscreen(true);
    210     auto surface = testContext.surface();
    211     int width, height;
    212     surface->query(NATIVE_WINDOW_WIDTH, &width);
    213     surface->query(NATIVE_WINDOW_HEIGHT, &height);
    214     canvasContext->setSurface(std::move(surface));
    215 
    216     TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
    217     DamageAccumulator damageAccumulator;
    218     info.damageAccumulator = &damageAccumulator;
    219 
    220     // The VectorDrawableRoot needs to have bounds on screen (and therefore not
    221     // empty) in order to have PropertyChangeWillBeConsumed set.
    222     const auto bounds = SkRect::MakeIWH(100, 100);
    223 
    224     for (const SkRect b : {bounds.makeOffset(width, 0),
    225                            bounds.makeOffset(0, height),
    226                            bounds.makeOffset(-bounds.width(), 0),
    227                            bounds.makeOffset(0, -bounds.height())}) {
    228         SkiaDisplayList skiaDL;
    229         VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
    230         dirtyVD.mutateProperties()->setBounds(b);
    231         skiaDL.appendVD(&dirtyVD);
    232 
    233         ASSERT_TRUE(dirtyVD.isDirty());
    234         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
    235 
    236         TestUtils::MockTreeObserver observer;
    237         ASSERT_FALSE(skiaDL.prepareListAndChildren(
    238                 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
    239         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
    240     }
    241 
    242     // The DamageAccumulator's transform can also result in the
    243     // VectorDrawableRoot being offscreen.
    244     for (const SkISize translate : { SkISize{width, 0},
    245                                      SkISize{0, height},
    246                                      SkISize{-width, 0},
    247                                      SkISize{0, -height}}) {
    248         Matrix4 mat4;
    249         mat4.translate(translate.fWidth, translate.fHeight);
    250         damageAccumulator.pushTransform(&mat4);
    251 
    252         SkiaDisplayList skiaDL;
    253         VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
    254         dirtyVD.mutateProperties()->setBounds(bounds);
    255         skiaDL.appendVD(&dirtyVD);
    256 
    257         ASSERT_TRUE(dirtyVD.isDirty());
    258         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
    259 
    260         TestUtils::MockTreeObserver observer;
    261         ASSERT_FALSE(skiaDL.prepareListAndChildren(
    262                 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
    263         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
    264         damageAccumulator.popTransform();
    265     }
    266 
    267     // Another way to be offscreen: a matrix from the draw call.
    268     for (const SkMatrix translate : { SkMatrix::MakeTrans(width, 0),
    269                                       SkMatrix::MakeTrans(0, height),
    270                                       SkMatrix::MakeTrans(-width, 0),
    271                                       SkMatrix::MakeTrans(0, -height)}) {
    272         SkiaDisplayList skiaDL;
    273         VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
    274         dirtyVD.mutateProperties()->setBounds(bounds);
    275         skiaDL.appendVD(&dirtyVD, translate);
    276 
    277         ASSERT_TRUE(dirtyVD.isDirty());
    278         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
    279 
    280         TestUtils::MockTreeObserver observer;
    281         ASSERT_FALSE(skiaDL.prepareListAndChildren(
    282                 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
    283         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
    284     }
    285 
    286     // Verify that the matrices are combined in the right order.
    287     {
    288         // Rotate and then translate, so the VD is offscreen.
    289         Matrix4 mat4;
    290         mat4.loadRotate(180);
    291         damageAccumulator.pushTransform(&mat4);
    292 
    293         SkiaDisplayList skiaDL;
    294         VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
    295         dirtyVD.mutateProperties()->setBounds(bounds);
    296         SkMatrix translate = SkMatrix::MakeTrans(50, 50);
    297         skiaDL.appendVD(&dirtyVD, translate);
    298 
    299         ASSERT_TRUE(dirtyVD.isDirty());
    300         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
    301 
    302         TestUtils::MockTreeObserver observer;
    303         ASSERT_FALSE(skiaDL.prepareListAndChildren(
    304                 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
    305         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
    306         damageAccumulator.popTransform();
    307     }
    308     {
    309         // Switch the order of rotate and translate, so it is on screen.
    310         Matrix4 mat4;
    311         mat4.translate(50, 50);
    312         damageAccumulator.pushTransform(&mat4);
    313 
    314         SkiaDisplayList skiaDL;
    315         VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
    316         dirtyVD.mutateProperties()->setBounds(bounds);
    317         SkMatrix rotate;
    318         rotate.setRotate(180);
    319         skiaDL.appendVD(&dirtyVD, rotate);
    320 
    321         ASSERT_TRUE(dirtyVD.isDirty());
    322         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
    323 
    324         TestUtils::MockTreeObserver observer;
    325         ASSERT_TRUE(skiaDL.prepareListAndChildren(
    326                 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
    327         ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
    328         damageAccumulator.popTransform();
    329     }
    330     {
    331         // An AVD that is larger than the screen.
    332         SkiaDisplayList skiaDL;
    333         VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
    334         dirtyVD.mutateProperties()->setBounds(SkRect::MakeLTRB(-1, -1, width + 1, height + 1));
    335         skiaDL.appendVD(&dirtyVD);
    336 
    337         ASSERT_TRUE(dirtyVD.isDirty());
    338         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
    339 
    340         TestUtils::MockTreeObserver observer;
    341         ASSERT_TRUE(skiaDL.prepareListAndChildren(
    342                 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
    343         ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
    344     }
    345     {
    346         // An AVD whose bounds are not a rectangle after applying a matrix.
    347         SkiaDisplayList skiaDL;
    348         VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
    349         dirtyVD.mutateProperties()->setBounds(bounds);
    350         SkMatrix mat;
    351         mat.setRotate(45, 50, 50);
    352         skiaDL.appendVD(&dirtyVD, mat);
    353 
    354         ASSERT_TRUE(dirtyVD.isDirty());
    355         ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
    356 
    357         TestUtils::MockTreeObserver observer;
    358         ASSERT_TRUE(skiaDL.prepareListAndChildren(
    359                 observer, info, false, [](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
    360         ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
    361     }
    362 }
    363 
    364 TEST(SkiaDisplayList, updateChildren) {
    365     SkiaDisplayList skiaDL;
    366 
    367     sp<RenderNode> renderNode = new RenderNode();
    368     SkCanvas dummyCanvas;
    369     skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas);
    370     skiaDL.updateChildren([renderNode](RenderNode* n) { ASSERT_EQ(renderNode.get(), n); });
    371 }
    372