/* * Copyright (C) 2014 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 #include #include #include #include #include #include #include #include "TestContext.h" #include #include using namespace android; using namespace android::uirenderer; using namespace android::uirenderer::renderthread; using namespace android::uirenderer::test; class ContextFactory : public IContextFactory { public: virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override { return new AnimationContext(clock); } }; static DisplayListCanvas* startRecording(RenderNode* node) { DisplayListCanvas* renderer = new DisplayListCanvas(); renderer->setViewport(node->stagingProperties().getWidth(), node->stagingProperties().getHeight()); renderer->prepare(); return renderer; } static void endRecording(DisplayListCanvas* renderer, RenderNode* node) { renderer->finish(); node->setStagingDisplayList(renderer->finishRecording()); delete renderer; } class TreeContentAnimation { public: virtual ~TreeContentAnimation() {} int frameCount = 150; virtual int getFrameCount() { return frameCount; } virtual void setFrameCount(int fc) { if (fc > 0) { frameCount = fc; } } virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0; virtual void doFrame(int frameNr) = 0; template static void run(int frameCount) { T animation; animation.setFrameCount(frameCount); TestContext testContext; // create the native surface const int width = gDisplay.w; const int height = gDisplay.h; sp surface = testContext.surface(); RenderNode* rootNode = new RenderNode(); rootNode->incStrong(nullptr); rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height); rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); rootNode->mutateStagingProperties().setClipToBounds(false); rootNode->setPropertyFieldsDirty(RenderNode::GENERIC); ContextFactory factory; std::unique_ptr proxy(new RenderProxy(false, rootNode, &factory)); proxy->loadSystemProperties(); proxy->initialize(surface); float lightX = width / 2.0; proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15); proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)}); android::uirenderer::Rect DUMMY; DisplayListCanvas* renderer = startRecording(rootNode); animation.createContent(width, height, renderer); endRecording(renderer, rootNode); // Do a few cold runs then reset the stats so that the caches are all hot for (int i = 0; i < 3; i++) { testContext.waitForVsync(); proxy->syncAndDrawFrame(); } proxy->resetProfileInfo(); for (int i = 0; i < animation.getFrameCount(); i++) { testContext.waitForVsync(); // workaround b/20853441 proxy->fence(); ATRACE_NAME("UI-Draw Frame"); nsecs_t vsync = systemTime(CLOCK_MONOTONIC); UiFrameInfoBuilder(proxy->frameInfo()) .setVsync(vsync, vsync); animation.doFrame(i); proxy->syncAndDrawFrame(); } proxy->dumpProfileInfo(STDOUT_FILENO, 0); rootNode->decStrong(nullptr); } }; class ShadowGridAnimation : public TreeContentAnimation { public: std::vector< sp > cards; void createContent(int width, int height, DisplayListCanvas* renderer) override { renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); renderer->insertReorderBarrier(true); for (int x = dp(16); x < (width - dp(116)); x += dp(116)) { for (int y = dp(16); y < (height - dp(116)); y += dp(116)) { sp card = createCard(x, y, dp(100), dp(100)); renderer->drawRenderNode(card.get()); cards.push_back(card); } } renderer->insertReorderBarrier(false); } void doFrame(int frameNr) override { int curFrame = frameNr % 150; for (size_t ci = 0; ci < cards.size(); ci++) { cards[ci]->mutateStagingProperties().setTranslationX(curFrame); cards[ci]->mutateStagingProperties().setTranslationY(curFrame); cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); } } private: sp createCard(int x, int y, int width, int height) { sp node = new RenderNode(); node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); node->mutateStagingProperties().setElevation(dp(16)); node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1); node->mutateStagingProperties().mutableOutline().setShouldClip(true); node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z); DisplayListCanvas* renderer = startRecording(node.get()); renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode); endRecording(renderer, node.get()); return node; } }; class ShadowGrid2Animation : public TreeContentAnimation { public: std::vector< sp > cards; void createContent(int width, int height, DisplayListCanvas* renderer) override { renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); renderer->insertReorderBarrier(true); for (int x = dp(8); x < (width - dp(58)); x += dp(58)) { for (int y = dp(8); y < (height - dp(58)); y += dp(58)) { sp card = createCard(x, y, dp(50), dp(50)); renderer->drawRenderNode(card.get()); cards.push_back(card); } } renderer->insertReorderBarrier(false); } void doFrame(int frameNr) override { int curFrame = frameNr % 150; for (size_t ci = 0; ci < cards.size(); ci++) { cards[ci]->mutateStagingProperties().setTranslationX(curFrame); cards[ci]->mutateStagingProperties().setTranslationY(curFrame); cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); } } private: sp createCard(int x, int y, int width, int height) { sp node = new RenderNode(); node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); node->mutateStagingProperties().setElevation(dp(16)); node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1); node->mutateStagingProperties().mutableOutline().setShouldClip(true); node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z); DisplayListCanvas* renderer = startRecording(node.get()); renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode); endRecording(renderer, node.get()); return node; } }; class RectGridAnimation : public TreeContentAnimation { public: sp card; void createContent(int width, int height, DisplayListCanvas* renderer) override { renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); renderer->insertReorderBarrier(true); card = createCard(40, 40, 200, 200); renderer->drawRenderNode(card.get()); renderer->insertReorderBarrier(false); } void doFrame(int frameNr) override { int curFrame = frameNr % 150; card->mutateStagingProperties().setTranslationX(curFrame); card->mutateStagingProperties().setTranslationY(curFrame); card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); } private: sp createCard(int x, int y, int width, int height) { sp node = new RenderNode(); node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); DisplayListCanvas* renderer = startRecording(node.get()); renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode); float rects[width * height]; int index = 0; for (int xOffset = 0; xOffset < width; xOffset+=2) { for (int yOffset = 0; yOffset < height; yOffset+=2) { rects[index++] = xOffset; rects[index++] = yOffset; rects[index++] = xOffset + 1; rects[index++] = yOffset + 1; } } int count = width * height; SkPaint paint; paint.setColor(0xff00ffff); renderer->drawRects(rects, count, &paint); endRecording(renderer, node.get()); return node; } }; class OvalAnimation : public TreeContentAnimation { public: sp card; void createContent(int width, int height, DisplayListCanvas* renderer) override { renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); renderer->insertReorderBarrier(true); card = createCard(40, 40, 400, 400); renderer->drawRenderNode(card.get()); renderer->insertReorderBarrier(false); } void doFrame(int frameNr) override { int curFrame = frameNr % 150; card->mutateStagingProperties().setTranslationX(curFrame); card->mutateStagingProperties().setTranslationY(curFrame); card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); } private: sp createCard(int x, int y, int width, int height) { sp node = new RenderNode(); node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); DisplayListCanvas* renderer = startRecording(node.get()); SkPaint paint; paint.setAntiAlias(true); paint.setColor(0xFF000000); renderer->drawOval(0, 0, width, height, paint); endRecording(renderer, node.get()); return node; } }; struct cstr_cmp { bool operator()(const char *a, const char *b) const { return std::strcmp(a, b) < 0; } }; typedef void (*testProc)(int); std::map gTestMap { {"shadowgrid", TreeContentAnimation::run}, {"shadowgrid2", TreeContentAnimation::run}, {"rectgrid", TreeContentAnimation::run }, {"oval", TreeContentAnimation::run }, }; int main(int argc, char* argv[]) { const char* testName = argc > 1 ? argv[1] : "shadowgrid"; testProc proc = gTestMap[testName]; if(!proc) { printf("Error: couldn't find test %s\n", testName); return 1; } int loopCount = 1; if (argc > 2) { loopCount = atoi(argv[2]); if (!loopCount) { printf("Invalid loop count!\n"); return 1; } } int frameCount = 150; if (argc > 3) { frameCount = atoi(argv[3]); if (frameCount < 1) { printf("Invalid frame count!\n"); return 1; } } if (loopCount < 0) { loopCount = INT_MAX; } for (int i = 0; i < loopCount; i++) { proc(frameCount); } printf("Success!\n"); return 0; }