Add a way to iterate over a specific op type
Also split the OpBuffer tests out from CanvasOpTests
Test: this
Change-Id: I0fbc726d108bfa4333c01daf7aedacca8716e6ae
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 0533aa6..9d82f63 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -618,6 +618,7 @@
"tests/unit/LayerUpdateQueueTests.cpp",
"tests/unit/LinearAllocatorTests.cpp",
"tests/unit/MatrixTests.cpp",
+ "tests/unit/OpBufferTests.cpp",
"tests/unit/PathInterpolatorTests.cpp",
"tests/unit/RenderNodeDrawableTests.cpp",
"tests/unit/RenderNodeTests.cpp",
diff --git a/libs/hwui/canvas/OpBuffer.h b/libs/hwui/canvas/OpBuffer.h
index 6dc29d9..1237d69 100644
--- a/libs/hwui/canvas/OpBuffer.h
+++ b/libs/hwui/canvas/OpBuffer.h
@@ -144,7 +144,86 @@
ItemHeader* last() const { return isEmpty() ? nullptr : itemAt(mBuffer->endOffset); }
+ class sentinal {
+ public:
+ explicit sentinal(const uint8_t* end) : end(end) {}
+ private:
+ const uint8_t* const end;
+ };
+
+ sentinal end() const {
+ return sentinal{end_ptr()};
+ }
+
+ template <ItemTypes T>
+ class filtered_iterator {
+ public:
+ explicit filtered_iterator(uint8_t* start, const uint8_t* end)
+ : mCurrent(start), mEnd(end) {
+ ItemHeader* header = reinterpret_cast<ItemHeader*>(mCurrent);
+ if (header->type != T) {
+ advance();
+ }
+ }
+
+ filtered_iterator& operator++() {
+ advance();
+ return *this;
+ }
+
+ // Although this iterator self-terminates, we need a placeholder to compare against
+ // to make for-each loops happy
+ bool operator!=(const sentinal& other) const {
+ return mCurrent != mEnd;
+ }
+
+ ItemContainer<T>& operator*() {
+ return *reinterpret_cast<ItemContainer<T>*>(mCurrent);
+ }
+ private:
+ void advance() {
+ ItemHeader* header = reinterpret_cast<ItemHeader*>(mCurrent);
+ do {
+ mCurrent += header->size;
+ header = reinterpret_cast<ItemHeader*>(mCurrent);
+ } while (mCurrent != mEnd && header->type != T);
+ }
+ uint8_t* mCurrent;
+ const uint8_t* const mEnd;
+ };
+
+ template <ItemTypes T>
+ class filtered_view {
+ public:
+ explicit filtered_view(uint8_t* start, const uint8_t* end) : mStart(start), mEnd(end) {}
+
+ filtered_iterator<T> begin() const {
+ return filtered_iterator<T>{mStart, mEnd};
+ }
+
+ sentinal end() const {
+ return sentinal{mEnd};
+ }
+ private:
+ uint8_t* mStart;
+ const uint8_t* const mEnd;
+ };
+
+ template <ItemTypes T>
+ filtered_view<T> filter() const {
+ return filtered_view<T>{start_ptr(), end_ptr()};
+ }
+
private:
+
+ uint8_t* start_ptr() const {
+ return reinterpret_cast<uint8_t*>(mBuffer) + mBuffer->startOffset;
+ }
+
+ const uint8_t* end_ptr() const {
+ return reinterpret_cast<uint8_t*>(mBuffer) + mBuffer->used;
+ }
+
template <typename F, std::size_t... I>
void for_each(F&& f, std::index_sequence<I...>) const {
// Validate we're not empty
@@ -159,8 +238,8 @@
}...};
// Do the actual iteration of each item
- uint8_t* current = reinterpret_cast<uint8_t*>(mBuffer) + mBuffer->startOffset;
- uint8_t* end = reinterpret_cast<uint8_t*>(mBuffer) + mBuffer->used;
+ uint8_t* current = start_ptr();
+ const uint8_t* end = end_ptr();
while (current != end) {
auto header = reinterpret_cast<ItemHeader*>(current);
// `f` could be a destructor, so ensure all accesses to the OP happen prior to invoking
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index 033a587..a09e742 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -36,50 +36,6 @@
// 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>;
-
class CanvasOpCountingReceiver {
public:
template <CanvasOpType T>
@@ -104,62 +60,6 @@
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, verifyConst) {
CanvasOpBuffer buffer;
buffer.push<Op::DrawColor>({
@@ -708,7 +608,3 @@
EXPECT_EQ(1, receiver[Op::Save]);
EXPECT_EQ(1, receiver[Op::Restore]);
}
-
-TEST(CanvasOp, frontendTransform) {
-
-}
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/OpBufferTests.cpp b/libs/hwui/tests/unit/OpBufferTests.cpp
new file mode 100644
index 0000000..c0ae943
--- /dev/null
+++ b/libs/hwui/tests/unit/OpBufferTests.cpp
@@ -0,0 +1,190 @@
+/*
+ * 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/OpBuffer.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+enum MockTypes {
+ Lifecycle,
+ NoOp,
+ IntHolder,
+ COUNT
+};
+
+using Op = MockTypes;
+
+template<MockTypes T>
+struct MockOp;
+
+template<MockTypes T>
+struct MockOpContainer {
+ OpBufferItemHeader<MockTypes> header;
+ MockOp<T> impl;
+
+ MockOpContainer(MockOp<T>&& impl) : impl(std::move(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;
+};
+
+template<>
+struct MockOp<MockTypes::NoOp> {};
+
+template<>
+struct MockOp<MockTypes::IntHolder> {
+ int value = -1;
+};
+
+struct MockBuffer : public OpBuffer<MockTypes, MockOpContainer> {
+ template <MockTypes T>
+ void push(MockOp<T>&& op) {
+ push_container(MockOpContainer<T>{std::move(op)});
+ }
+};
+
+template<typename T>
+static int countItems(const T& t) {
+ int count = 0;
+ t.for_each([&](auto i) {
+ count++;
+ });
+ return count;
+}
+
+TEST(OpBuffer, lifecycleCheck) {
+ LifecycleTracker tracker;
+ {
+ MockBuffer buffer;
+ buffer.push_container(MockOpContainer<Op::Lifecycle> {
+ MockOp<MockTypes::Lifecycle>{&tracker}
+ });
+ EXPECT_EQ(tracker.alive(), 1);
+ buffer.clear();
+ EXPECT_EQ(tracker.alive(), 0);
+ }
+ EXPECT_EQ(tracker.alive(), 0);
+}
+
+TEST(OpBuffer, lifecycleCheckMove) {
+ LifecycleTracker tracker;
+ {
+ MockBuffer buffer;
+ buffer.push_container(MockOpContainer<Op::Lifecycle> {
+ 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> {
+ MockOp<MockTypes::Lifecycle>{&tracker}
+ });
+
+ EXPECT_EQ(2, countItems(other));
+ EXPECT_EQ(2, tracker.alive());
+
+ buffer.push_container(MockOpContainer<MockTypes::Lifecycle> {
+ 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(OpBuffer, verifyConst) {
+ MockBuffer buffer;
+ buffer.push<Op::IntHolder>({42});
+ buffer.for_each([](auto op) {
+ static_assert(std::is_const_v<std::remove_reference_t<decltype(*op)>>,
+ "Expected container to be const");
+ });
+}
+
+TEST(OpBuffer, filterView) {
+ MockBuffer buffer;
+ buffer.push<Op::NoOp>({});
+ buffer.push<Op::IntHolder>({0});
+ buffer.push<Op::IntHolder>({1});
+ buffer.push<Op::NoOp>({});
+ buffer.push<Op::NoOp>({});
+ buffer.push<Op::IntHolder>({2});
+ buffer.push<Op::NoOp>({});
+ buffer.push<Op::NoOp>({});
+ buffer.push<Op::NoOp>({});
+ buffer.push<Op::NoOp>({});
+
+
+ int index = 0;
+ for (const auto& it : buffer.filter<Op::IntHolder>()) {
+ ASSERT_EQ(Op::IntHolder, it.header.type);
+ EXPECT_EQ(index, it.impl.value);
+ index++;
+ }
+ EXPECT_EQ(index, 3);
+
+ int count = 0;
+ for (const auto& it : buffer.filter<Op::NoOp>()) {
+ ASSERT_EQ(Op::NoOp, it.header.type);
+ count++;
+ }
+ EXPECT_EQ(count, 7);
+}
+