blob: 415f9e8517ffb21e2373fc6abf5c17912b5dd599 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <VectorDrawable.h>
#include <gtest/gtest.h>
#include "AnimationContext.h"
#include "DamageAccumulator.h"
#include "IContextFactory.h"
#include "pipeline/skia/GLFunctorDrawable.h"
#include "pipeline/skia/SkiaDisplayList.h"
#include "renderthread/CanvasContext.h"
#include "tests/common/TestUtils.h"
using namespace android;
using namespace android::uirenderer;
using namespace android::uirenderer::renderthread;
using namespace android::uirenderer::skiapipeline;
TEST(SkiaDisplayList, create) {
SkiaDisplayList skiaDL;
ASSERT_TRUE(skiaDL.isEmpty());
ASSERT_FALSE(skiaDL.mProjectionReceiver);
}
TEST(SkiaDisplayList, reset) {
std::unique_ptr<SkiaDisplayList> skiaDL;
{
SkiaRecordingCanvas canvas{nullptr, 1, 1};
canvas.drawColor(0, SkBlendMode::kSrc);
skiaDL.reset(canvas.finishRecording());
}
SkCanvas dummyCanvas;
RenderNodeDrawable drawable(nullptr, &dummyCanvas);
skiaDL->mChildNodes.emplace_back(nullptr, &dummyCanvas);
GLFunctorDrawable functorDrawable(nullptr, nullptr, &dummyCanvas);
skiaDL->mChildFunctors.push_back(&functorDrawable);
skiaDL->mMutableImages.push_back(nullptr);
skiaDL->mVectorDrawables.push_back(nullptr);
skiaDL->mProjectionReceiver = &drawable;
ASSERT_FALSE(skiaDL->mChildNodes.empty());
ASSERT_FALSE(skiaDL->mChildFunctors.empty());
ASSERT_FALSE(skiaDL->mMutableImages.empty());
ASSERT_FALSE(skiaDL->mVectorDrawables.empty());
ASSERT_FALSE(skiaDL->isEmpty());
ASSERT_TRUE(skiaDL->mProjectionReceiver);
skiaDL->reset();
ASSERT_TRUE(skiaDL->mChildNodes.empty());
ASSERT_TRUE(skiaDL->mChildFunctors.empty());
ASSERT_TRUE(skiaDL->mMutableImages.empty());
ASSERT_TRUE(skiaDL->mVectorDrawables.empty());
ASSERT_TRUE(skiaDL->isEmpty());
ASSERT_FALSE(skiaDL->mProjectionReceiver);
}
TEST(SkiaDisplayList, reuseDisplayList) {
sp<RenderNode> renderNode = new RenderNode();
std::unique_ptr<SkiaDisplayList> availableList;
// no list has been attached so it should return a nullptr
availableList = renderNode->detachAvailableList();
ASSERT_EQ(availableList.get(), nullptr);
// attach a displayList for reuse
SkiaDisplayList skiaDL;
ASSERT_TRUE(skiaDL.reuseDisplayList(renderNode.get(), nullptr));
// detach the list that you just attempted to reuse
availableList = renderNode->detachAvailableList();
ASSERT_EQ(availableList.get(), &skiaDL);
availableList.release(); // prevents an invalid free since our DL is stack allocated
// after detaching there should return no available list
availableList = renderNode->detachAvailableList();
ASSERT_EQ(availableList.get(), nullptr);
}
TEST(SkiaDisplayList, syncContexts) {
SkiaDisplayList skiaDL;
SkCanvas dummyCanvas;
TestUtils::MockFunctor functor;
GLFunctorDrawable functorDrawable(&functor, nullptr, &dummyCanvas);
skiaDL.mChildFunctors.push_back(&functorDrawable);
SkRect bounds = SkRect::MakeWH(200, 200);
VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
vectorDrawable.mutateStagingProperties()->setBounds(bounds);
skiaDL.mVectorDrawables.push_back(&vectorDrawable);
// ensure that the functor and vectorDrawable are properly synced
skiaDL.syncContents();
ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync);
ASSERT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
}
class ContextFactory : public IContextFactory {
public:
virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
return new AnimationContext(clock);
}
};
RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) {
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
SkiaDisplayList skiaDL;
// prepare with a clean VD
VectorDrawableRoot cleanVD(new VectorDrawable::Group());
skiaDL.mVectorDrawables.push_back(&cleanVD);
cleanVD.getBitmapUpdateIfDirty(); // this clears the dirty bit
ASSERT_FALSE(cleanVD.isDirty());
ASSERT_FALSE(cleanVD.getPropertyChangeWillBeConsumed());
TestUtils::MockTreeObserver observer;
ASSERT_FALSE(skiaDL.prepareListAndChildren(observer, info, false,
[](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
ASSERT_TRUE(cleanVD.getPropertyChangeWillBeConsumed());
// prepare again this time adding a dirty VD
VectorDrawableRoot dirtyVD(new VectorDrawable::Group());
skiaDL.mVectorDrawables.push_back(&dirtyVD);
ASSERT_TRUE(dirtyVD.isDirty());
ASSERT_FALSE(dirtyVD.getPropertyChangeWillBeConsumed());
ASSERT_TRUE(skiaDL.prepareListAndChildren(observer, info, false,
[](RenderNode*, TreeObserver&, TreeInfo&, bool) {}));
ASSERT_TRUE(dirtyVD.getPropertyChangeWillBeConsumed());
// prepare again this time adding a RenderNode and a callback
sp<RenderNode> renderNode = new RenderNode();
TreeInfo* infoPtr = &info;
SkCanvas dummyCanvas;
skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas);
bool hasRun = false;
ASSERT_TRUE(skiaDL.prepareListAndChildren(
observer, info, false,
[&hasRun, renderNode, infoPtr](RenderNode* n, TreeObserver& observer, TreeInfo& i,
bool r) {
hasRun = true;
ASSERT_EQ(renderNode.get(), n);
ASSERT_EQ(infoPtr, &i);
ASSERT_FALSE(r);
}));
ASSERT_TRUE(hasRun);
canvasContext->destroy();
}
TEST(SkiaDisplayList, updateChildren) {
SkiaDisplayList skiaDL;
sp<RenderNode> renderNode = new RenderNode();
SkCanvas dummyCanvas;
skiaDL.mChildNodes.emplace_back(renderNode.get(), &dummyCanvas);
skiaDL.updateChildren([renderNode](RenderNode* n) { ASSERT_EQ(renderNode.get(), n); });
}