Add ListView rendering benchmark

Also fixes a bug in DrawRenderNodeOp recording, which was triggered by
the new test.

Change-Id: I328f2ed908495eb95ca8ce87a365d02650e72cd5
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index bd11d0a..e7cc464 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1383,7 +1383,10 @@
     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 b416615..b7dd3b7 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 <cutils/compiler.h>
 #include <array>
@@ -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 @@
 };
 
 static const std::array<BarSegment,7> 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 @@
     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 61fa384..69c686e 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -435,8 +435,9 @@
             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 0000000..27adb12
--- /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 <cstdio>
+
+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<ListViewAnimation>
+});
+
+class ListViewAnimation : public TestScene {
+public:
+    int cardHeight;
+    int cardSpacing;
+    int cardWidth;
+    int cardLeft;
+    sp<RenderNode> listView;
+    std::vector< sp<RenderNode> > 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<RenderNode> 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 1e38d84..5d4ef96 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 @@
 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 @@
             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 @@
             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 0000000..b5157f4
--- /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 <SkColor.h>
+
+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 */