DL-next groundwork
Initial groundwork / tests for the op-forward displaylist
Test: hwuiunit --gtest_filter=CanvasOpBuffer.*
Change-Id: I6a09d9841c964a67fde8203b979de3fd3fbd2026
diff --git a/libs/hwui/canvas/CanvasOpBuffer.cpp b/libs/hwui/canvas/CanvasOpBuffer.cpp
new file mode 100644
index 0000000..7054e47e
--- /dev/null
+++ b/libs/hwui/canvas/CanvasOpBuffer.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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 "CanvasOpBuffer.h"
+
+#include "CanvasOps.h"
+
+namespace android::uirenderer {
+
+template class OpBuffer<CanvasOpType, CanvasOpContainer>;
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/canvas/CanvasOpBuffer.h b/libs/hwui/canvas/CanvasOpBuffer.h
new file mode 100644
index 0000000..b80faeb
--- /dev/null
+++ b/libs/hwui/canvas/CanvasOpBuffer.h
@@ -0,0 +1,60 @@
+/*
+ * 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 <SkMatrix.h>
+
+#include "CanvasOpTypes.h"
+#include "OpBuffer.h"
+
+namespace android::uirenderer {
+
+template <CanvasOpType T>
+struct CanvasOp;
+
+template <CanvasOpType T>
+class CanvasOpContainer {
+private:
+ BE_OPBUFFERS_FRIEND();
+
+ OpBufferItemHeader<CanvasOpType> header;
+ // TODO: Figure out some magic to make this not be here when it's identity (or not used)
+ SkMatrix mTransform;
+ CanvasOp<T> mImpl;
+
+public:
+ CanvasOpContainer(CanvasOp<T>&& impl, const SkMatrix& transform = SkMatrix::I())
+ : mTransform(transform), mImpl(std::move(impl)) {}
+
+ uint32_t size() const { return header.size; }
+ CanvasOpType type() const { return header.type; }
+
+ const SkMatrix& transform() const { return mTransform; }
+
+ CanvasOp<T>* operator->() noexcept { return &mImpl; }
+};
+
+extern template class OpBuffer<CanvasOpType, CanvasOpContainer>;
+class CanvasOpBuffer final : public OpBuffer<CanvasOpType, CanvasOpContainer> {
+public:
+ template <CanvasOpType T>
+ void push(CanvasOp<T>&& op) {
+ push_container(CanvasOpContainer<T>(std::move(op)));
+ }
+};
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/canvas/CanvasOpRasterizer.cpp b/libs/hwui/canvas/CanvasOpRasterizer.cpp
new file mode 100644
index 0000000..97c418a
--- /dev/null
+++ b/libs/hwui/canvas/CanvasOpRasterizer.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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 "CanvasOpRasterizer.h"
+
+#include <SkCanvas.h>
+#include <log/log.h>
+
+#include <vector>
+
+#include "CanvasOpBuffer.h"
+#include "CanvasOps.h"
+
+namespace android::uirenderer {
+
+void rasterizeCanvasBuffer(const CanvasOpBuffer& source, SkCanvas* destination) {
+ // Tracks the global transform from the current display list back toward the display space
+ // Push on beginning a RenderNode draw, pop on ending one
+ std::vector<SkMatrix> globalMatrixStack;
+ SkMatrix& currentGlobalTransform = globalMatrixStack.emplace_back(SkMatrix::I());
+
+ source.for_each([&]<CanvasOpType T>(CanvasOpContainer<T> * op) {
+ if constexpr (T == CanvasOpType::BeginZ || T == CanvasOpType::EndZ) {
+ // Do beginZ or endZ
+ LOG_ALWAYS_FATAL("TODO");
+ return;
+ } else {
+ // Generic OP
+ // First apply the current transformation
+ destination->setMatrix(SkMatrix::Concat(currentGlobalTransform, op->transform()));
+ // Now draw it
+ (*op)->draw(destination);
+ }
+ });
+}
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/canvas/CanvasOpRasterizer.h b/libs/hwui/canvas/CanvasOpRasterizer.h
new file mode 100644
index 0000000..c2235ab
--- /dev/null
+++ b/libs/hwui/canvas/CanvasOpRasterizer.h
@@ -0,0 +1,64 @@
+/*
+ * 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 <hwui/Bitmap.h>
+
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+
+#include "CanvasOps.h"
+
+#include <experimental/type_traits>
+#include <variant>
+
+namespace android::uirenderer {
+
+class CanvasOpBuffer;
+
+void rasterizeCanvasBuffer(const CanvasOpBuffer& source, SkCanvas* destination);
+
+class ImmediateModeRasterizer {
+public:
+ explicit ImmediateModeRasterizer(std::unique_ptr<SkCanvas>&& canvas) {
+ mCanvas = canvas.get();
+ mOwnership = std::move(canvas);
+ }
+
+ explicit ImmediateModeRasterizer(std::shared_ptr<SkCanvas> canvas) {
+ mCanvas = canvas.get();
+ mOwnership = std::move(canvas);
+ }
+
+ explicit ImmediateModeRasterizer(Bitmap& bitmap) {
+ mCanvas = &(mOwnership.emplace<SkCanvas>(bitmap.getSkBitmap()));
+ }
+
+ template <CanvasOpType T>
+ void draw(const CanvasOp<T>& op) {
+ if constexpr (CanvasOpTraits::can_draw<CanvasOp<T>>) {
+ op.draw(mCanvas);
+ }
+ }
+
+private:
+ SkCanvas* mCanvas;
+ // Just here to keep mCanvas alive. Thankfully we never need to actually look inside this...
+ std::variant<SkCanvas, std::shared_ptr<SkCanvas>, std::unique_ptr<SkCanvas>> mOwnership;
+};
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/canvas/CanvasOpTypes.h b/libs/hwui/canvas/CanvasOpTypes.h
new file mode 100644
index 0000000..2d4f2f5
--- /dev/null
+++ b/libs/hwui/canvas/CanvasOpTypes.h
@@ -0,0 +1,46 @@
+/*
+ * 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 <inttypes.h>
+
+namespace android::uirenderer {
+
+enum class CanvasOpType : int8_t {
+ // State ops
+ // TODO: Eliminate the end ops by having the start include the end-at position
+ Save,
+ SaveLayer,
+ SaveBehind,
+ Restore,
+ BeginZ,
+ EndZ,
+
+ // Clip ops
+ ClipRect,
+ ClipPath,
+
+ // Drawing ops
+ DrawColor,
+ DrawRect,
+
+ // TODO: Rest
+
+ COUNT // must be last
+};
+
+} // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h
new file mode 100644
index 0000000..a31a91c
--- /dev/null
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -0,0 +1,126 @@
+/*
+ * 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 <SkAndroidFrameworkUtils.h>
+#include <SkCanvas.h>
+#include <SkPath.h>
+#include <log/log.h>
+
+#include "CanvasOpTypes.h"
+
+#include <experimental/type_traits>
+
+namespace android::uirenderer {
+
+template <CanvasOpType T>
+struct CanvasOp;
+
+struct CanvasOpTraits {
+ CanvasOpTraits() = delete;
+
+ template<class T>
+ using draw_t = decltype(std::integral_constant<void (T::*)(SkCanvas*) const, &T::draw>{});
+
+ template <class T>
+ static constexpr bool can_draw = std::experimental::is_detected_v<draw_t, T>;
+};
+
+#define ASSERT_DRAWABLE() private: constexpr void _check_drawable() \
+ { static_assert(CanvasOpTraits::can_draw<std::decay_t<decltype(*this)>>); }
+
+// ----------------------------------------------
+// State Ops
+// ---------------------------------------------
+
+template <>
+struct CanvasOp<CanvasOpType::Save> {
+ void draw(SkCanvas* canvas) const { canvas->save(); }
+ ASSERT_DRAWABLE()
+};
+
+template <>
+struct CanvasOp<CanvasOpType::SaveLayer> {
+ SkCanvas::SaveLayerRec saveLayerRec;
+ void draw(SkCanvas* canvas) const { canvas->saveLayer(saveLayerRec); }
+ ASSERT_DRAWABLE()
+};
+
+template <>
+struct CanvasOp<CanvasOpType::SaveBehind> {
+ SkRect bounds;
+ void draw(SkCanvas* canvas) const { SkAndroidFrameworkUtils::SaveBehind(canvas, &bounds); }
+ ASSERT_DRAWABLE()
+};
+
+template <>
+struct CanvasOp<CanvasOpType::Restore> {
+ void draw(SkCanvas* canvas) const { canvas->restore(); }
+ ASSERT_DRAWABLE()
+};
+
+template <>
+struct CanvasOp<CanvasOpType::BeginZ> {
+};
+template <>
+struct CanvasOp<CanvasOpType::EndZ> {};
+
+// ----------------------------------------------
+// Clip Ops
+// ---------------------------------------------
+
+template <>
+struct CanvasOp<CanvasOpType::ClipRect> {
+ SkRect rect;
+ SkClipOp clipOp;
+ void draw(SkCanvas* canvas) const { canvas->clipRect(rect, clipOp); }
+ ASSERT_DRAWABLE()
+};
+
+template <>
+struct CanvasOp<CanvasOpType::ClipPath> {
+ SkPath path;
+ SkClipOp op;
+ void draw(SkCanvas* canvas) const { canvas->clipPath(path, op, true); }
+ ASSERT_DRAWABLE()
+};
+
+// ----------------------------------------------
+// Drawing Ops
+// ---------------------------------------------
+
+template <>
+struct CanvasOp<CanvasOpType::DrawColor> {
+ SkColor4f color;
+ SkBlendMode mode;
+ void draw(SkCanvas* canvas) const { canvas->drawColor(color, mode); }
+ ASSERT_DRAWABLE()
+};
+
+template <>
+struct CanvasOp<CanvasOpType::DrawRect> {
+ SkRect rect;
+ SkPaint paint;
+ void draw(SkCanvas* canvas) const { canvas->drawRect(rect, paint); }
+ ASSERT_DRAWABLE()
+};
+
+
+// cleanup our macros
+#undef ASSERT_DRAWABLE
+
+} // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/canvas/OpBuffer.h b/libs/hwui/canvas/OpBuffer.h
new file mode 100644
index 0000000..398e090
--- /dev/null
+++ b/libs/hwui/canvas/OpBuffer.h
@@ -0,0 +1,210 @@
+/*
+ * 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 <algorithm>
+#include <array>
+#include <cinttypes>
+#include <cstddef>
+#include <cstdlib>
+#include <type_traits>
+#include <utility>
+
+namespace android::uirenderer {
+
+template <typename T>
+struct OpBufferItemHeader {
+ T type : 8;
+ uint32_t size : 24;
+};
+
+struct OpBufferAllocationHeader {
+ // Used size, including header size
+ size_t used = 0;
+ // Capacity, including header size
+ size_t capacity = 0;
+ // Offset relative to `this` at which the first item is
+ size_t startOffset = 0;
+ // Offset relative to `this` at which the last item is
+ size_t endOffset = 0;
+};
+
+#define BE_OPBUFFERS_FRIEND() \
+ template <typename ItemTypes, template <ItemTypes> typename, typename, typename> \
+ friend class OpBuffer
+
+template <typename ItemTypes, template <ItemTypes> typename ItemContainer,
+ typename BufferHeader = OpBufferAllocationHeader,
+ typename ItemTypesSequence = std::make_index_sequence<static_cast<int>(ItemTypes::COUNT)>>
+class OpBuffer {
+ // Instead of re-aligning individual inserts, just pad the size of everything
+ // to a multiple of pointer alignment. This assumes we never work with doubles.
+ // Which we don't.
+ static constexpr size_t Alignment = alignof(void*);
+
+ static constexpr size_t PadAlign(size_t size) {
+ return (size + (Alignment - 1)) & -Alignment;
+ }
+
+ static constexpr auto STARTING_SIZE = PadAlign(sizeof(BufferHeader));
+
+public:
+ using ItemHeader = OpBufferItemHeader<ItemTypes>;
+
+ OpBuffer() = default;
+
+ // Prevent copying by default
+ OpBuffer(const OpBuffer&) = delete;
+ void operator=(const OpBuffer&) = delete;
+
+ OpBuffer(OpBuffer&& other) {
+ mBuffer = other.mBuffer;
+ other.mBuffer = nullptr;
+ }
+
+ void operator=(OpBuffer&& other) {
+ destroy();
+ mBuffer = other.mBuffer;
+ other.mBuffer = nullptr;
+ }
+
+ ~OpBuffer() {
+ destroy();
+ }
+
+ constexpr size_t capacity() const { return mBuffer ? mBuffer->capacity : 0; }
+
+ constexpr size_t size() const { return mBuffer ? mBuffer->used : 0; }
+
+ constexpr size_t remaining() const { return capacity() - size(); }
+
+ // TODO: Add less-copy'ing variants of this. emplace_back? deferred initialization?
+ template <ItemTypes T>
+ void push_container(ItemContainer<T>&& op) {
+ static_assert(alignof(ItemContainer<T>) <= Alignment);
+ static_assert(offsetof(ItemContainer<T>, header) == 0);
+
+ constexpr auto padded_size = PadAlign(sizeof(ItemContainer<T>));
+ if (remaining() < padded_size) {
+ resize(std::max(padded_size, capacity()) * 2);
+ }
+ mBuffer->endOffset = mBuffer->used;
+ mBuffer->used += padded_size;
+
+ void* allocateAt = reinterpret_cast<uint8_t*>(mBuffer) + mBuffer->endOffset;
+ auto temp = new (allocateAt) ItemContainer<T>{std::move(op)};
+ temp->header = {.type = T, .size = padded_size};
+ }
+
+ void resize(size_t newsize) {
+ // Add the header size to newsize
+ const size_t adjustedSize = newsize + STARTING_SIZE;
+
+ if (adjustedSize < size()) {
+ // todo: throw?
+ return;
+ }
+ if (newsize == 0) {
+ free(mBuffer);
+ mBuffer = nullptr;
+ } else {
+ if (mBuffer) {
+ mBuffer = reinterpret_cast<BufferHeader*>(realloc(mBuffer, adjustedSize));
+ mBuffer->capacity = adjustedSize;
+ } else {
+ mBuffer = new (malloc(adjustedSize)) BufferHeader();
+ mBuffer->capacity = adjustedSize;
+ mBuffer->used = STARTING_SIZE;
+ mBuffer->startOffset = STARTING_SIZE;
+ }
+ }
+ }
+
+ template <typename F>
+ void for_each(F&& f) const {
+ for_each(std::forward<F>(f), ItemTypesSequence{});
+ }
+
+ void clear();
+
+ ItemHeader* first() const { return isEmpty() ? nullptr : itemAt(mBuffer->startOffset); }
+
+ ItemHeader* last() const { return isEmpty() ? nullptr : itemAt(mBuffer->endOffset); }
+
+private:
+ template <typename F, std::size_t... I>
+ void for_each(F&& f, std::index_sequence<I...>) const {
+ // Validate we're not empty
+ if (isEmpty()) return;
+
+ // Setup the jump table, mapping from each type to a springboard that invokes the template
+ // function with the appropriate concrete type
+ using F_PTR = decltype(&f);
+ using THUNK = void (*)(F_PTR, void*);
+ static constexpr auto jump = std::array<THUNK, sizeof...(I)>{[](F_PTR fp, void* t) {
+ (*fp)(reinterpret_cast<ItemContainer<static_cast<ItemTypes>(I)>*>(t));
+ }...};
+
+ // 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;
+ 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
+ // `f`
+ auto it = (void*)current;
+ current += header->size;
+ jump[static_cast<int>(header->type)](&f, it);
+ }
+ }
+
+ void destroy() {
+ clear();
+ resize(0);
+ }
+
+ bool offsetIsValid(size_t offset) const {
+ return offset >= mBuffer->startOffset && offset < mBuffer->used;
+ }
+
+ ItemHeader* itemAt(size_t offset) const {
+ if (!offsetIsValid(offset)) return nullptr;
+ return reinterpret_cast<ItemHeader*>(reinterpret_cast<uint8_t*>(mBuffer) + offset);
+ }
+
+ bool isEmpty() const { return mBuffer == nullptr || mBuffer->used == STARTING_SIZE; }
+
+ BufferHeader* mBuffer = nullptr;
+};
+
+template <typename ItemTypes, template <ItemTypes> typename ItemContainer, typename BufferHeader,
+ typename ItemTypeSequence>
+void OpBuffer<ItemTypes, ItemContainer, BufferHeader, ItemTypeSequence>::clear() {
+
+ // Don't need to do anything if we don't have a buffer
+ if (!mBuffer) return;
+
+ for_each([](auto op) {
+ using T = std::remove_reference_t<decltype(*op)>;
+ op->~T();
+ });
+ mBuffer->used = STARTING_SIZE;
+ mBuffer->startOffset = STARTING_SIZE;
+ mBuffer->endOffset = 0;
+}
+
+} // namespace android::uirenderer
\ No newline at end of file