From 54fa17f667c285a5c9225e238c8132dfe830ef36 Mon Sep 17 00:00:00 2001 From: Chris Craik Date: Wed, 25 Nov 2015 14:14:53 -0800 Subject: [PATCH] Add ListView rendering benchmark Also fixes a bug in DrawRenderNodeOp recording, which was triggered by the new test. Change-Id: I328f2ed908495eb95ca8ce87a365d02650e72cd5 --- libs/hwui/DisplayListOp.h | 5 +- libs/hwui/FrameInfoVisualizer.cpp | 31 +++--- libs/hwui/RecordingCanvas.cpp | 3 +- libs/hwui/tests/scenes/ListViewAnimation.cpp | 146 +++++++++++++++++++++++++++ libs/hwui/tests/scenes/RecentsAnimation.cpp | 15 +-- libs/hwui/utils/Color.h | 86 ++++++++++++++++ 6 files changed, 262 insertions(+), 24 deletions(-) create mode 100644 libs/hwui/tests/scenes/ListViewAnimation.cpp create mode 100644 libs/hwui/utils/Color.h diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index bd11d0acf176..e7cc464facd8 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -1383,7 +1383,10 @@ class DrawRenderNodeOp : public DrawBoundedOp { friend class TestUtils; public: DrawRenderNodeOp(RenderNode* renderNode, const mat4& transformFromParent, bool clipIsSimple) - : DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), nullptr) + : DrawBoundedOp(0, 0, + renderNode->stagingProperties().getWidth(), + renderNode->stagingProperties().getHeight(), + nullptr) , renderNode(renderNode) , mRecordedWithPotentialStencilClip(!clipIsSimple || !transformFromParent.isSimple()) , localMatrix(transformFromParent) diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp index b416615c20e1..b7dd3b7e2f95 100644 --- a/libs/hwui/FrameInfoVisualizer.cpp +++ b/libs/hwui/FrameInfoVisualizer.cpp @@ -16,6 +16,7 @@ #include "FrameInfoVisualizer.h" #include "OpenGLRenderer.h" +#include "utils/Color.h" #include #include @@ -27,19 +28,19 @@ #define PROFILE_DRAW_THRESHOLD_STROKE_WIDTH 2 #define PROFILE_DRAW_DP_PER_MS 7 +namespace android { +namespace uirenderer { + // Must be NUM_ELEMENTS in size -static const SkColor THRESHOLD_COLOR = 0xff5faa4d; -static const SkColor BAR_FAST_ALPHA = 0x8F000000; -static const SkColor BAR_JANKY_ALPHA = 0xDF000000; +static const SkColor THRESHOLD_COLOR = Color::Green_500; +static const SkColor BAR_FAST_MASK = 0x8FFFFFFF; +static const SkColor BAR_JANKY_MASK = 0xDFFFFFFF; // We could get this from TimeLord and use the actual frame interval, but // this is good enough #define FRAME_THRESHOLD 16 #define FRAME_THRESHOLD_NS 16000000 -namespace android { -namespace uirenderer { - struct BarSegment { FrameInfoIndex start; FrameInfoIndex end; @@ -47,13 +48,13 @@ struct BarSegment { }; static const std::array Bar {{ - { FrameInfoIndex::IntendedVsync, FrameInfoIndex::HandleInputStart, 0x00796B }, - { FrameInfoIndex::HandleInputStart, FrameInfoIndex::PerformTraversalsStart, 0x388E3C }, - { FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, 0x689F38}, - { FrameInfoIndex::DrawStart, FrameInfoIndex::SyncStart, 0x2196F3}, - { FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart, 0x4FC3F7}, - { FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers, 0xF44336}, - { FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted, 0xFF9800}, + { FrameInfoIndex::IntendedVsync, FrameInfoIndex::HandleInputStart, Color::Teal_700 }, + { FrameInfoIndex::HandleInputStart, FrameInfoIndex::PerformTraversalsStart, Color::Green_700 }, + { FrameInfoIndex::PerformTraversalsStart, FrameInfoIndex::DrawStart, Color::LightGreen_700 }, + { FrameInfoIndex::DrawStart, FrameInfoIndex::SyncStart, Color::Blue_500 }, + { FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart, Color::LightBlue_300 }, + { FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers, Color::Red_500}, + { FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted, Color::Orange_500}, }}; static int dpToPx(int dp, float density) { @@ -197,9 +198,9 @@ void FrameInfoVisualizer::drawGraph(OpenGLRenderer* canvas) { SkPaint paint; for (size_t i = 0; i < Bar.size(); i++) { nextBarSegment(Bar[i].start, Bar[i].end); - paint.setColor(Bar[i].color | BAR_FAST_ALPHA); + paint.setColor(Bar[i].color & BAR_FAST_MASK); canvas->drawRects(mFastRects.get(), mNumFastRects * 4, &paint); - paint.setColor(Bar[i].color | BAR_JANKY_ALPHA); + paint.setColor(Bar[i].color & BAR_JANKY_MASK); canvas->drawRects(mJankyRects.get(), mNumJankyRects * 4, &paint); } } diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 61fa3844df27..69c686ec3ba2 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -435,8 +435,9 @@ void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) { refPaint(paint), refBitmap(*bitmap))); } void RecordingCanvas::drawRenderNode(RenderNode* renderNode) { + auto&& stagingProps = renderNode->stagingProperties(); RenderNodeOp* op = new (alloc()) RenderNodeOp( - Rect(0, 0, renderNode->getWidth(), renderNode->getHeight()), // are these safe? they're theoretically dynamic + Rect(stagingProps.getWidth(), stagingProps.getHeight()), *(mState.currentSnapshot()->transform), mState.getRenderTargetClipBounds(), renderNode); diff --git a/libs/hwui/tests/scenes/ListViewAnimation.cpp b/libs/hwui/tests/scenes/ListViewAnimation.cpp new file mode 100644 index 000000000000..27adb125fcf9 --- /dev/null +++ b/libs/hwui/tests/scenes/ListViewAnimation.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2015 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 "TestSceneBase.h" +#include "utils/Color.h" + +#include + +class ListViewAnimation; + +static Benchmark _ListView(BenchmarkInfo{ + "listview", + "A mock ListView of scrolling content. Doesn't re-bind/re-record views as they are recycled, so" + "won't upload much content (either glyphs, or bitmaps).", + simpleCreateScene +}); + +class ListViewAnimation : public TestScene { +public: + int cardHeight; + int cardSpacing; + int cardWidth; + int cardLeft; + sp listView; + std::vector< sp > cards; + void createContent(int width, int height, TestCanvas& canvas) override { + srand(0); + cardHeight = dp(60); + cardSpacing = dp(16); + cardWidth = std::min((height - cardSpacing * 2), (int)dp(300)); + cardLeft = (width - cardWidth) / 2; + + for (int y = 0; y < height + (cardHeight + cardSpacing - 1); y += (cardHeight + cardSpacing)) { + cards.push_back(createCard(cards.size(), y)); + } + listView = TestUtils::createNode(0, 0, width, height, + [this](RenderProperties& props, TestCanvas& canvas) { + for (size_t ci = 0; ci < cards.size(); ci++) { + canvas.drawRenderNode(cards[ci].get()); + } + }); + + canvas.drawColor(Color::Grey_500, SkXfermode::kSrcOver_Mode); + canvas.drawRenderNode(listView.get()); + } + + void doFrame(int frameNr) override { + int scrollPx = dp(frameNr) * 3; + int cardIndexOffset = scrollPx / (cardSpacing + cardHeight); + int pxOffset = -(scrollPx % (cardSpacing + cardHeight)); + + TestCanvas canvas(cardWidth, cardHeight); + for (size_t ci = 0; ci < cards.size(); ci++) { + // update card position + auto card = cards[(ci + cardIndexOffset) % cards.size()]; + int top = ((int)ci) * (cardSpacing + cardHeight) + pxOffset; + card->mutateStagingProperties().setLeftTopRightBottom( + cardLeft, top, cardLeft + cardWidth, top + cardHeight); + card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + + // draw it to parent DisplayList + canvas.drawRenderNode(cards[ci].get()); + } + listView->setStagingDisplayList(canvas.finishRecording()); + } +private: + SkBitmap createRandomCharIcon() { + int size = cardHeight - (dp(10) * 2); + SkBitmap bitmap = TestUtils::createSkBitmap(size, size); + SkCanvas canvas(bitmap); + canvas.clear(0); + + SkPaint paint; + paint.setAntiAlias(true); + SkColor randomColor = BrightColors[rand() % BrightColorsCount]; + paint.setColor(randomColor); + canvas.drawCircle(size / 2, size / 2, size / 2, paint); + + bool bgDark = SkColorGetR(randomColor) + SkColorGetG(randomColor) + SkColorGetB(randomColor) + < 128 * 3; + paint.setColor(bgDark ? Color::White : Color::Grey_700); + paint.setTextAlign(SkPaint::kCenter_Align); + paint.setTextSize(size / 2); + char charToShow = 'A' + (rand() % 26); + canvas.drawText(&charToShow, 1, size / 2, /*approximate centering*/ size * 0.7, paint); + return bitmap; + } + + static SkBitmap createBoxBitmap(bool filled) { + int size = dp(20); + int stroke = dp(2); + SkBitmap bitmap = TestUtils::createSkBitmap(size, size); + SkCanvas canvas(bitmap); + canvas.clear(Color::Transparent); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setColor(filled ? Color::Yellow_500 : Color::Grey_700); + paint.setStyle(filled ? SkPaint::kStrokeAndFill_Style : SkPaint::kStroke_Style); + paint.setStrokeWidth(stroke); + canvas.drawRect(SkRect::MakeLTRB(stroke, stroke, size - stroke, size - stroke), paint); + return bitmap; + } + + sp createCard(int cardId, int top) { + return TestUtils::createNode(cardLeft, top, cardLeft + cardWidth, top + cardHeight, + [this, cardId](RenderProperties& props, TestCanvas& canvas) { + static SkBitmap filledBox = createBoxBitmap(true); + static SkBitmap strokedBox = createBoxBitmap(false); + + props.mutableOutline().setRoundRect(0, 0, cardWidth, cardHeight, dp(6), 1); + props.mutableOutline().setShouldClip(true); + canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode); + + SkPaint textPaint; + textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + textPaint.setColor(rand() % 2 ? Color::Black : Color::Grey_500); + textPaint.setTextSize(dp(20)); + textPaint.setAntiAlias(true); + char buf[256]; + snprintf(buf, sizeof(buf), "This card is #%d", cardId); + TestUtils::drawTextToCanvas(&canvas, buf, textPaint, cardHeight, dp(25)); + textPaint.setTextSize(dp(15)); + TestUtils::drawTextToCanvas(&canvas, "This is some more text on the card", textPaint, + cardHeight, dp(45)); + + canvas.drawBitmap(createRandomCharIcon(), dp(10), dp(10), nullptr); + + const SkBitmap& boxBitmap = rand() % 2 ? filledBox : strokedBox; + canvas.drawBitmap(boxBitmap, cardWidth - dp(10) - boxBitmap.width(), dp(10), nullptr); + }); + } +}; diff --git a/libs/hwui/tests/scenes/RecentsAnimation.cpp b/libs/hwui/tests/scenes/RecentsAnimation.cpp index 1e38d84dac1f..5d4ef9663d34 100644 --- a/libs/hwui/tests/scenes/RecentsAnimation.cpp +++ b/libs/hwui/tests/scenes/RecentsAnimation.cpp @@ -15,6 +15,7 @@ */ #include "TestSceneBase.h" +#include "utils/Color.h" class RecentsAnimation; @@ -29,16 +30,16 @@ class RecentsAnimation : public TestScene { public: void createContent(int width, int height, TestCanvas& renderer) override { static SkColor COLORS[] = { - 0xFFF44336, - 0xFF9C27B0, - 0xFF2196F3, - 0xFF4CAF50, + Color::Red_500, + Color::Purple_500, + Color::Blue_500, + Color::Green_500, }; thumbnailSize = std::min(std::min(width, height) / 2, 720); int cardsize = std::min(width, height) - dp(64); - renderer.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + renderer.drawColor(Color::White, SkXfermode::kSrcOver_Mode); renderer.insertReorderBarrier(true); int x = dp(32); @@ -63,7 +64,7 @@ public: mCards[ci]->setPropertyFieldsDirty(RenderNode::Y); } mThumbnail.eraseColor(TestUtils::interpolateColor( - curFrame / 150.0f, 0xFF4CAF50, 0xFFFF5722)); + curFrame / 150.0f, Color::Green_500, Color::DeepOrange_500)); } private: @@ -75,7 +76,7 @@ private: props.mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1); props.mutableOutline().setShouldClip(true); - canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode); + canvas.drawColor(Color::Grey_200, SkXfermode::kSrcOver_Mode); canvas.drawBitmap(thumb, 0, 0, thumb.width(), thumb.height(), 0, 0, width, height, nullptr); }); diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h new file mode 100644 index 000000000000..b5157f401438 --- /dev/null +++ b/libs/hwui/utils/Color.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015 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. + */ +#ifndef COLOR_H +#define COLOR_H + +#include + +namespace android { +namespace uirenderer { + namespace Color { + enum Color { + Red_500 = 0xFFF44336, + Pink_500 = 0xFFE91E63, + Purple_500 = 0xFF9C27B0, + DeepPurple_500 = 0xFF673AB7, + Indigo_500 = 0xFF3F51B5, + Blue_500 = 0xFF2196F3, + LightBlue_300 = 0xFF4FC3F7, + LightBlue_500 = 0xFF03A9F4, + Cyan_500 = 0xFF00BCD4, + Teal_500 = 0xFF009688, + Teal_700 = 0xFF00796B, + Green_500 = 0xFF4CAF50, + Green_700 = 0xFF388E3C, + LightGreen_500 = 0xFF8BC34A, + LightGreen_700 = 0xFF689F38, + Lime_500 = 0xFFCDDC39, + Yellow_500 = 0xFFFFEB3B, + Amber_500 = 0xFFFFC107, + Orange_500 = 0xFFFF9800, + DeepOrange_500 = 0xFFFF5722, + Brown_500 = 0xFF795548, + Grey_200 = 0xFFEEEEEE, + Grey_500 = 0xFF9E9E9E, + Grey_700 = 0xFF616161, + BlueGrey_500 = 0xFF607D8B, + Transparent = 0x00000000, + Black = 0xFF000000, + White = 0xFFFFFFFF, + }; + } + + static_assert(Color::White == SK_ColorWHITE, "color format has changed"); + static_assert(Color::Black == SK_ColorBLACK, "color format has changed"); + + // Array of bright (500 intensity) colors for synthetic content + static const Color::Color BrightColors[] = { + Color::Red_500, + Color::Pink_500, + Color::Purple_500, + Color::DeepPurple_500, + Color::Indigo_500, + Color::Blue_500, + Color::LightBlue_500, + Color::Cyan_500, + Color::Teal_500, + Color::Green_500, + Color::LightGreen_500, + Color::Lime_500, + Color::Yellow_500, + Color::Amber_500, + Color::Orange_500, + Color::DeepOrange_500, + Color::Brown_500, + Color::Grey_500, + Color::BlueGrey_500, + }; + static constexpr int BrightColorsCount = sizeof(BrightColors) / sizeof(Color::Color); + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* TEST_UTILS_H */ -- 2.11.0