Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9573505a authored by John Reck's avatar John Reck
Browse files

Add a way to iterate over a specific op type

Also split the OpBuffer tests out from CanvasOpTests

Test: this
Change-Id: I0fbc726d108bfa4333c01daf7aedacca8716e6ae
parent e9faa339
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -618,6 +618,7 @@ cc_test {
        "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",
+81 −2
Original line number Diff line number Diff line
@@ -144,7 +144,86 @@ public:

    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 @@ private:
        }...};

        // 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
+0 −104
Original line number Diff line number Diff line
@@ -36,50 +36,6 @@ 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>;

class CanvasOpCountingReceiver {
public:
    template <CanvasOpType T>
@@ -104,62 +60,6 @@ static int countItems(const T& t) {
    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 @@ TEST(CanvasOp, frontendSaveCount) {
    EXPECT_EQ(1, receiver[Op::Save]);
    EXPECT_EQ(1, receiver[Op::Restore]);
}

TEST(CanvasOp, frontendTransform) {

}
 No newline at end of file
+190 −0
Original line number Diff line number Diff line
/*
 * 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);
}