Add initial benchmarks for CanvasOp

Also some minor other tweaks

Test: this
Change-Id: Idb8a5955839893ff000de87d4899fd130ede061c
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 1ff1978..0533aa6 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -672,6 +672,7 @@
 
     srcs: [
         "tests/microbench/main.cpp",
+        "tests/microbench/CanvasOpBench.cpp",
         "tests/microbench/DisplayListCanvasBench.cpp",
         "tests/microbench/LinearAllocatorBench.cpp",
         "tests/microbench/PathParserBench.cpp",
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index dc63e5d..dd24763 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -16,14 +16,15 @@
 
 #pragma once
 
-#include "pipeline/skia/SkiaDisplayList.h"
-
 namespace android {
 namespace uirenderer {
 
 namespace VectorDrawable {
 class Tree;
 };
+namespace skiapipeline {
+class SkiaDisplayList;
+}
 typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
 
 /**
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 31e4555..74c70c8 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -380,7 +380,7 @@
     if (mDisplayList) {
         mDisplayList->updateChildren(
                 [&observer, info](RenderNode* child) { child->decParentRefCount(observer, info); });
-        if (!mDisplayList->reuseDisplayList(this, info ? &info->canvasContext : nullptr)) {
+        if (!mDisplayList->reuseDisplayList(this)) {
             delete mDisplayList;
         }
     }
diff --git a/libs/hwui/canvas/CanvasFrontend.cpp b/libs/hwui/canvas/CanvasFrontend.cpp
index 2c839b0..8f261c83 100644
--- a/libs/hwui/canvas/CanvasFrontend.cpp
+++ b/libs/hwui/canvas/CanvasFrontend.cpp
@@ -21,7 +21,14 @@
 namespace android::uirenderer {
 
 CanvasStateHelper::CanvasStateHelper(int width, int height) {
+    resetState(width, height);
+}
+
+void CanvasStateHelper::resetState(int width, int height) {
     mInitialBounds = SkIRect::MakeWH(width, height);
+    mSaveStack.clear();
+    mClipStack.clear();
+    mTransformStack.clear();
     mSaveStack.emplace_back();
     mClipStack.emplace_back().setRect(mInitialBounds);
     mTransformStack.emplace_back();
diff --git a/libs/hwui/canvas/CanvasFrontend.h b/libs/hwui/canvas/CanvasFrontend.h
index 5fccccb..d749d2f 100644
--- a/libs/hwui/canvas/CanvasFrontend.h
+++ b/libs/hwui/canvas/CanvasFrontend.h
@@ -57,9 +57,7 @@
     }
 
     bool internalSave(SaveEntry saveEntry);
-    bool internalSave(SaveFlags::Flags flags) {
-        return internalSave(flagsToSaveEntry(flags));
-    }
+
     void internalSaveLayer(const SkCanvas::SaveLayerRec& layerRec) {
         internalSave({
             .clip = true,
@@ -90,6 +88,8 @@
         return mClipStack[mCurrentClipIndex];
     }
 
+    void resetState(int width, int height);
+
 public:
     int saveCount() const { return mSaveStack.size(); }
 
@@ -186,14 +186,26 @@
         submit(std::move(op));
     }
 
-    const CanvasOpReceiver& receiver() const { return mReceiver; }
+    const CanvasOpReceiver& receiver() const { return *mReceiver; }
+
+    CanvasOpReceiver finish() {
+        auto ret = std::move(mReceiver.value());
+        mReceiver.reset();
+        return std::move(ret);
+    }
+
+    template<class... Args>
+    void reset(int newWidth, int newHeight, Args&&... args) {
+        resetState(newWidth, newHeight);
+        mReceiver.emplace(std::forward<Args>(args)...);
+    }
 
 private:
-    CanvasOpReceiver mReceiver;
+    std::optional<CanvasOpReceiver> mReceiver;
 
     template <CanvasOpType T>
     void submit(CanvasOp<T>&& op) {
-        mReceiver.push_container(CanvasOpContainer(std::move(op), transform()));
+        mReceiver->push_container(CanvasOpContainer(std::move(op), transform()));
     }
 };
 
diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h
index b499733..e2c5eaa 100644
--- a/libs/hwui/canvas/CanvasOps.h
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -277,7 +277,7 @@
 template<>
 struct CanvasOp<CanvasOpType::DrawImage> {
 
-    CanvasOp<CanvasOpType::DrawImageRect>(
+    CanvasOp(
         const sk_sp<Bitmap>& bitmap,
         float left,
         float top,
@@ -303,7 +303,7 @@
 template<>
 struct CanvasOp<CanvasOpType::DrawImageRect> {
 
-    CanvasOp<CanvasOpType::DrawImageRect>(
+    CanvasOp(
         const sk_sp<Bitmap>& bitmap,
         SkRect src,
         SkRect dst,
@@ -334,7 +334,7 @@
 template<>
 struct CanvasOp<CanvasOpType::DrawImageLattice> {
 
-    CanvasOp<CanvasOpType::DrawImageLattice>(
+    CanvasOp(
         const sk_sp<Bitmap>& bitmap,
         SkRect dst,
         SkCanvas::Lattice lattice,
diff --git a/libs/hwui/canvas/OpBuffer.h b/libs/hwui/canvas/OpBuffer.h
index 98e385f..6dc29d9 100644
--- a/libs/hwui/canvas/OpBuffer.h
+++ b/libs/hwui/canvas/OpBuffer.h
@@ -60,9 +60,8 @@
         return (size + (Alignment - 1)) & -Alignment;
     }
 
-    static constexpr auto STARTING_SIZE = PadAlign(sizeof(BufferHeader));
-
 public:
+    static constexpr auto STARTING_SIZE = PadAlign(sizeof(BufferHeader));
     using ItemHeader = OpBufferItemHeader<ItemTypes>;
 
     OpBuffer() = default;
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 4d67166..11fa322 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -21,6 +21,7 @@
 #include <SaveFlags.h>
 
 #include <androidfw/ResourceTypes.h>
+#include "DisplayList.h"
 #include "Properties.h"
 #include "utils/Macros.h"
 
@@ -47,18 +48,6 @@
 class CanvasPropertyPrimitive;
 class DeferredLayerUpdater;
 class RenderNode;
-
-namespace skiapipeline {
-class SkiaDisplayList;
-}
-
-/**
- * Data structure that holds the list of commands used in display list stream
- */
-using DisplayList = skiapipeline::SkiaDisplayList;
-}
-
-namespace uirenderer {
 namespace VectorDrawable {
 class Tree;
 }
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 158c349..c63f5d3 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -47,7 +47,7 @@
     }
 }
 
-bool SkiaDisplayList::reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) {
+bool SkiaDisplayList::reuseDisplayList(RenderNode* node) {
     reset();
     node->attachAvailableList(this);
     return true;
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index cdd00db..f2f19ba 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -98,7 +98,7 @@
      *
      * @return true if the displayList will be reused and therefore should not be deleted
      */
-    bool reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context);
+    bool reuseDisplayList(RenderNode* node);
 
     /**
      * ONLY to be called by RenderNode::syncDisplayList so that we can notify any
diff --git a/libs/hwui/tests/microbench/CanvasOpBench.cpp b/libs/hwui/tests/microbench/CanvasOpBench.cpp
new file mode 100644
index 0000000..ef5749e
--- /dev/null
+++ b/libs/hwui/tests/microbench/CanvasOpBench.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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 <benchmark/benchmark.h>
+
+#include "DisplayList.h"
+#include "hwui/Paint.h"
+#include "canvas/CanvasOpBuffer.h"
+#include "canvas/CanvasFrontend.h"
+#include "tests/common/TestUtils.h"
+
+using namespace android;
+using namespace android::uirenderer;
+
+void BM_CanvasOpBuffer_alloc(benchmark::State& benchState) {
+    while (benchState.KeepRunning()) {
+        auto displayList = new CanvasOpBuffer();
+        benchmark::DoNotOptimize(displayList);
+        delete displayList;
+    }
+}
+BENCHMARK(BM_CanvasOpBuffer_alloc);
+
+void BM_CanvasOpBuffer_record_saverestore(benchmark::State& benchState) {
+    CanvasFrontend<CanvasOpBuffer> canvas(100, 100);
+    while (benchState.KeepRunning()) {
+        canvas.reset(100, 100);
+        canvas.save(SaveFlags::MatrixClip);
+        canvas.save(SaveFlags::MatrixClip);
+        benchmark::DoNotOptimize(&canvas);
+        canvas.restore();
+        canvas.restore();
+        canvas.finish();
+    }
+}
+BENCHMARK(BM_CanvasOpBuffer_record_saverestore);
+
+void BM_CanvasOpBuffer_record_saverestoreWithReuse(benchmark::State& benchState) {
+    CanvasFrontend<CanvasOpBuffer> canvas(100, 100);
+
+    while (benchState.KeepRunning()) {
+        canvas.reset(100, 100);
+        canvas.save(SaveFlags::MatrixClip);
+        canvas.save(SaveFlags::MatrixClip);
+        benchmark::DoNotOptimize(&canvas);
+        canvas.restore();
+        canvas.restore();
+    }
+}
+BENCHMARK(BM_CanvasOpBuffer_record_saverestoreWithReuse);
+
+void BM_CanvasOpBuffer_record_simpleBitmapView(benchmark::State& benchState) {
+    CanvasFrontend<CanvasOpBuffer> canvas(100, 100);
+
+    Paint rectPaint;
+    sk_sp<Bitmap> iconBitmap(TestUtils::createBitmap(80, 80));
+
+    while (benchState.KeepRunning()) {
+        canvas.reset(100, 100);
+        {
+            canvas.save(SaveFlags::MatrixClip);
+            canvas.draw(CanvasOp<CanvasOpType::DrawRect> {
+                    .rect = SkRect::MakeWH(100, 100),
+                    .paint = rectPaint,
+            });
+            canvas.restore();
+        }
+        {
+            canvas.save(SaveFlags::MatrixClip);
+            canvas.translate(10, 10);
+            canvas.draw(CanvasOp<CanvasOpType::DrawImage> {
+                    iconBitmap,
+                    0,
+                    0,
+                    SkPaint{}
+            });
+            canvas.restore();
+        }
+        benchmark::DoNotOptimize(&canvas);
+        canvas.finish();
+    }
+}
+BENCHMARK(BM_CanvasOpBuffer_record_simpleBitmapView);
diff --git a/libs/hwui/tests/microbench/RenderNodeBench.cpp b/libs/hwui/tests/microbench/RenderNodeBench.cpp
index 206dcd5..011939a 100644
--- a/libs/hwui/tests/microbench/RenderNodeBench.cpp
+++ b/libs/hwui/tests/microbench/RenderNodeBench.cpp
@@ -30,3 +30,29 @@
     }
 }
 BENCHMARK(BM_RenderNode_create);
+
+void BM_RenderNode_recordSimple(benchmark::State& state) {
+    sp<RenderNode> node = new RenderNode();
+    std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100));
+    delete canvas->finishRecording();
+
+    while (state.KeepRunning()) {
+        canvas->resetRecording(100, 100, node.get());
+        canvas->drawColor(0x00000000, SkBlendMode::kSrcOver);
+        node->setStagingDisplayList(canvas->finishRecording());
+    }
+}
+BENCHMARK(BM_RenderNode_recordSimple);
+
+void BM_RenderNode_recordSimpleWithReuse(benchmark::State& state) {
+    sp<RenderNode> node = new RenderNode();
+    std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100));
+    delete canvas->finishRecording();
+
+    while (state.KeepRunning()) {
+        canvas->resetRecording(100, 100, node.get());
+        canvas->drawColor(0x00000000, SkBlendMode::kSrcOver);
+        canvas->finishRecording()->reuseDisplayList(node.get());
+    }
+}
+BENCHMARK(BM_RenderNode_recordSimpleWithReuse);
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 2d34b09..c63f008 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -84,7 +84,7 @@
 
     // attach a displayList for reuse
     SkiaDisplayList skiaDL;
-    ASSERT_TRUE(skiaDL.reuseDisplayList(renderNode.get(), nullptr));
+    ASSERT_TRUE(skiaDL.reuseDisplayList(renderNode.get()));
 
     // detach the list that you just attempted to reuse
     availableList = renderNode->detachAvailableList();