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();