DL-next groundwork

Initial groundwork / tests for the op-forward displaylist

Test: hwuiunit --gtest_filter=CanvasOpBuffer.*
Change-Id: I6a09d9841c964a67fde8203b979de3fd3fbd2026
diff --git a/libs/hwui/tests/common/CallCountingCanvas.h b/libs/hwui/tests/common/CallCountingCanvas.h
new file mode 100644
index 0000000..a965571
--- /dev/null
+++ b/libs/hwui/tests/common/CallCountingCanvas.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <SkCanvasVirtualEnforcer.h>
+#include <SkNoDrawCanvas.h>
+
+namespace android {
+namespace uirenderer {
+namespace test {
+
+class CallCountingCanvas final : public SkCanvasVirtualEnforcer<SkNoDrawCanvas> {
+private:
+    int START_MARKER;
+public:
+    CallCountingCanvas() : SkCanvasVirtualEnforcer<SkNoDrawCanvas>(1, 1) {}
+
+    int sumTotalDrawCalls() {
+        // Dirty hack assumes we're nothing but ints between START_MARKET and END_MARKER
+        int* cur = &START_MARKER + 1;
+        int* end = &END_MARKER;
+        int sum = 0;
+        while (cur != end) {
+            sum += *cur;
+            cur++;
+        }
+        return sum;
+    }
+
+    int drawPaintCount = 0;
+    void onDrawPaint(const SkPaint& paint) override {
+        drawPaintCount++;
+    }
+
+    int drawBehindCount = 0;
+    void onDrawBehind(const SkPaint&) override {
+        drawBehindCount++;
+    }
+
+    int drawRectCount = 0;
+    void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
+        drawRectCount++;
+    }
+
+    int drawRRectCount = 0;
+    void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override {
+        drawRRectCount++;
+    }
+
+    int drawDRRectCount = 0;
+    void onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
+                      const SkPaint& paint) override {
+        drawDRRectCount++;
+    }
+
+    int drawOvalCount = 0;
+    void onDrawOval(const SkRect& rect, const SkPaint& paint) override {
+        drawOvalCount++;
+    }
+
+    int drawArcCount = 0;
+    void onDrawArc(const SkRect& rect, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
+                   const SkPaint& paint) override {
+        drawArcCount++;
+    }
+
+    int drawPathCount = 0;
+    void onDrawPath(const SkPath& path, const SkPaint& paint) override {
+        drawPaintCount++;
+    }
+
+    int drawRegionCount = 0;
+    void onDrawRegion(const SkRegion& region, const SkPaint& paint) override {
+        drawRegionCount++;
+    }
+
+    int drawTextBlobCount = 0;
+    void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
+                        const SkPaint& paint) override {
+        drawTextBlobCount++;
+    }
+
+    int drawPatchCount = 0;
+    void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
+                     const SkPoint texCoords[4], SkBlendMode mode,
+                     const SkPaint& paint) override {
+        drawPatchCount++;
+    }
+
+    int drawPoints = 0;
+    void onDrawPoints(SkCanvas::PointMode mode, size_t count, const SkPoint pts[],
+                      const SkPaint& paint) override {
+        drawPoints++;
+    }
+
+    int drawImageCount = 0;
+    void onDrawImage(const SkImage* image, SkScalar dx, SkScalar dy,
+                     const SkPaint* paint) override {
+        drawImageCount++;
+    }
+
+    int drawImageRectCount = 0;
+    void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
+                         const SkPaint* paint, SkCanvas::SrcRectConstraint constraint) override {
+        drawImageRectCount++;
+    }
+
+    int drawImageNineCount = 0;
+    void onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
+                         const SkPaint* paint) override {
+        drawImageNineCount++;
+    }
+
+    int drawImageLatticeCount = 0;
+    void onDrawImageLattice(const SkImage* image, const SkCanvas::Lattice& lattice,
+                            const SkRect& dst, const SkPaint* paint) override {
+        drawImageLatticeCount++;
+    }
+
+    int drawAtlasCount = 0;
+    void onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect rect[],
+                     const SkColor colors[], int count, SkBlendMode mode, const SkRect* cull,
+                     const SkPaint* paint) override {
+        drawAtlasCount++;
+    }
+
+    int drawAnnotationCount = 0;
+    void onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) override {
+        drawAnnotationCount++;
+    }
+
+    int drawShadowRecCount = 0;
+    void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override {
+        drawShadowRecCount++;
+    }
+
+    int drawDrawableCount = 0;
+    void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
+        drawDrawableCount++;
+    }
+
+    int drawPictureCount = 0;
+    void onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
+                       const SkPaint* paint) override {
+        drawPictureCount++;
+    }
+
+private:
+    int END_MARKER;
+};
+
+} /* namespace test */
+} /* namespace uirenderer */
+} /* namespace android */
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
new file mode 100644
index 0000000..0815d15
--- /dev/null
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2020 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 <gtest/gtest.h>
+
+#include <canvas/CanvasOpBuffer.h>
+#include <canvas/CanvasOps.h>
+#include <canvas/CanvasOpRasterizer.h>
+
+#include <tests/common/CallCountingCanvas.h>
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::test;
+
+// We lazy
+using Op = CanvasOpType;
+
+enum MockTypes {
+    Lifecycle,
+    COUNT
+};
+
+template<MockTypes T>
+struct MockOp;
+
+template<MockTypes T>
+struct MockOpContainer {
+    OpBufferItemHeader<MockTypes> header;
+    MockOp<T> impl;
+};
+
+struct LifecycleTracker {
+    int ctor_count = 0;
+    int dtor_count = 0;
+
+    int alive() { return ctor_count - dtor_count; }
+};
+
+template<>
+struct MockOp<MockTypes::Lifecycle> {
+    MockOp() = delete;
+    void operator=(const MockOp&) = delete;
+
+    MockOp(LifecycleTracker* tracker) : tracker(tracker) {
+        tracker->ctor_count += 1;
+    }
+
+    MockOp(const MockOp& other) {
+        tracker = other.tracker;
+        tracker->ctor_count += 1;
+    }
+
+    ~MockOp() {
+        tracker->dtor_count += 1;
+    }
+
+    LifecycleTracker* tracker = nullptr;
+};
+
+using MockBuffer = OpBuffer<MockTypes, MockOpContainer>;
+
+template<typename T>
+static int countItems(const T& t) {
+    int count = 0;
+    t.for_each([&](auto i) {
+        count++;
+    });
+    return count;
+}
+
+TEST(CanvasOp, lifecycleCheck) {
+    LifecycleTracker tracker;
+    {
+        MockBuffer buffer;
+        buffer.push_container(MockOpContainer<MockTypes::Lifecycle> {
+            .impl = MockOp<MockTypes::Lifecycle>{&tracker}
+        });
+        EXPECT_EQ(tracker.alive(), 1);
+        buffer.clear();
+        EXPECT_EQ(tracker.alive(), 0);
+    }
+    EXPECT_EQ(tracker.alive(), 0);
+}
+
+TEST(CanvasOp, lifecycleCheckMove) {
+    LifecycleTracker tracker;
+    {
+        MockBuffer buffer;
+        buffer.push_container(MockOpContainer<MockTypes::Lifecycle> {
+            .impl = MockOp<MockTypes::Lifecycle>{&tracker}
+        });
+        EXPECT_EQ(tracker.alive(), 1);
+        {
+            MockBuffer other(std::move(buffer));
+            EXPECT_EQ(tracker.alive(), 1);
+            EXPECT_EQ(buffer.size(), 0);
+            EXPECT_GT(other.size(), 0);
+            EXPECT_EQ(1, countItems(other));
+            EXPECT_EQ(0, countItems(buffer));
+
+            other.push_container(MockOpContainer<MockTypes::Lifecycle> {
+                .impl = MockOp<MockTypes::Lifecycle>{&tracker}
+            });
+
+            EXPECT_EQ(2, countItems(other));
+            EXPECT_EQ(2, tracker.alive());
+
+            buffer.push_container(MockOpContainer<MockTypes::Lifecycle> {
+                .impl = MockOp<MockTypes::Lifecycle>{&tracker}
+            });
+            EXPECT_EQ(1, countItems(buffer));
+            EXPECT_EQ(3, tracker.alive());
+
+            buffer = std::move(other);
+            EXPECT_EQ(2, countItems(buffer));
+            EXPECT_EQ(2, tracker.alive());
+        }
+        EXPECT_EQ(2, countItems(buffer));
+        EXPECT_EQ(2, tracker.alive());
+        buffer.clear();
+        EXPECT_EQ(0, countItems(buffer));
+        EXPECT_EQ(0, tracker.alive());
+    }
+    EXPECT_EQ(tracker.alive(), 0);
+}
+
+TEST(CanvasOp, simplePush) {
+    CanvasOpBuffer buffer;
+    EXPECT_EQ(buffer.size(), 0);
+    buffer.push<Op::Save>({});
+    buffer.push<Op::Save>({});
+    buffer.push<Op::Restore>({});
+    EXPECT_GT(buffer.size(), 0);
+
+    int saveCount = 0;
+    int restoreCount = 0;
+    int otherCount = 0;
+
+    buffer.for_each([&](auto op) {
+        switch (op->type()) {
+            case Op::Save:
+                saveCount++;
+                break;
+            case Op::Restore:
+                restoreCount++;
+                break;
+            default:
+                otherCount++;
+                break;
+        }
+    });
+
+    EXPECT_EQ(saveCount, 2);
+    EXPECT_EQ(restoreCount, 1);
+    EXPECT_EQ(otherCount, 0);
+
+    buffer.clear();
+    int itemCount = 0;
+    buffer.for_each([&](auto op) {
+        itemCount++;
+    });
+    EXPECT_EQ(itemCount, 0);
+    buffer.resize(0);
+    EXPECT_EQ(buffer.size(), 0);
+}
+
+TEST(CanvasOp, simpleDrawRect) {
+    CanvasOpBuffer buffer;
+    EXPECT_EQ(buffer.size(), 0);
+    buffer.push(CanvasOp<Op::DrawRect> {
+        .paint = SkPaint{},
+        .rect = SkRect::MakeEmpty()
+    });
+
+    CallCountingCanvas canvas;
+    EXPECT_EQ(0, canvas.sumTotalDrawCalls());
+    rasterizeCanvasBuffer(buffer, &canvas);
+    EXPECT_EQ(1, canvas.drawRectCount);
+    EXPECT_EQ(1, canvas.sumTotalDrawCalls());
+}
+
+TEST(CanvasOp, immediateRendering) {
+    auto canvas = std::make_shared<CallCountingCanvas>();
+
+    EXPECT_EQ(0, canvas->sumTotalDrawCalls());
+    ImmediateModeRasterizer rasterizer{canvas};
+    auto op = CanvasOp<Op::DrawRect> {
+        .paint = SkPaint{},
+        .rect = SkRect::MakeEmpty()
+    };
+    EXPECT_TRUE(CanvasOpTraits::can_draw<decltype(op)>);
+    rasterizer.draw(op);
+    EXPECT_EQ(1, canvas->drawRectCount);
+    EXPECT_EQ(1, canvas->sumTotalDrawCalls());
+}
\ No newline at end of file