diff options
author | 2021-01-19 21:29:24 -0500 | |
---|---|---|
committer | 2021-04-06 23:13:17 +0000 | |
commit | 064650b3bf5bf164f1de25bf28568c87823e406a (patch) | |
tree | 7011d7526f4ecd4896635aaa6ec292694061df2e | |
parent | 2cf7f6dacfd682b6dceefa012bd91ddd5ec0df6f (diff) |
Add MultiDisplayList + memory leak fixes
MultiDisplayList can contain either a SkiaDisplayList
or a CanvasOpBuffer. However DisplayList itself
still points to the SkiaDisplayList-only wrapper
to avoid any std::variant or std::visit overhead
just yet.
Also fixes a memory leak in CanvasFrontend from an
uninitialized std::optional and a few minor leaks
in unit tests.
Test: build & hwui_unit passes
Fixes: 184680809
Change-Id: Ifa6b723b6456f5d3eeac1201e76f337250103d6f
Merged-In: Ifa6b723b6456f5d3eeac1201e76f337250103d6f
(cherry picked from commit d34d6cec97c8f1be92f676aeb79c83d57cf8c6ba)
-rw-r--r-- | libs/hwui/Android.bp | 1 | ||||
-rw-r--r-- | libs/hwui/DisplayList.h | 189 | ||||
-rw-r--r-- | libs/hwui/canvas/CanvasFrontend.h | 9 | ||||
-rw-r--r-- | libs/hwui/canvas/CanvasOpBuffer.cpp | 28 | ||||
-rw-r--r-- | libs/hwui/canvas/CanvasOpBuffer.h | 71 | ||||
-rw-r--r-- | libs/hwui/canvas/CanvasOpRasterizer.cpp | 12 | ||||
-rw-r--r-- | libs/hwui/canvas/CanvasOpTypes.h | 9 | ||||
-rw-r--r-- | libs/hwui/canvas/CanvasOps.h | 13 | ||||
-rw-r--r-- | libs/hwui/canvas/OpBuffer.h | 6 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaDisplayList.h | 20 | ||||
-rw-r--r-- | libs/hwui/tests/unit/CanvasFrontendTests.cpp | 18 | ||||
-rw-r--r-- | libs/hwui/tests/unit/CanvasOpTests.cpp | 4 | ||||
-rw-r--r-- | libs/hwui/tests/unit/EglManagerTests.cpp | 44 | ||||
-rw-r--r-- | libs/hwui/tests/unit/SkiaPipelineTests.cpp | 1 |
14 files changed, 375 insertions, 50 deletions
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 607ef72df96a..9b9af6f75aeb 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -647,6 +647,7 @@ cc_test { "tests/unit/CommonPoolTests.cpp", "tests/unit/DamageAccumulatorTests.cpp", "tests/unit/DeferredLayerUpdaterTests.cpp", + "tests/unit/EglManagerTests.cpp", "tests/unit/FatVectorTests.cpp", "tests/unit/GraphicsStatsServiceTests.cpp", "tests/unit/LayerUpdateQueueTests.cpp", diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index 3aa5b4bf72f8..ca5f853066f2 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -17,8 +17,10 @@ #pragma once #include "pipeline/skia/SkiaDisplayList.h" +#include "canvas/CanvasOpBuffer.h" #include <memory> +#include <variant> namespace android { namespace uirenderer { @@ -28,29 +30,25 @@ class Tree; }; typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot; -/** - * Data structure that holds the list of commands used in display list stream - */ -//using DisplayList = skiapipeline::SkiaDisplayList; -class DisplayList { +class SkiaDisplayListWrapper { public: // Constructs an empty (invalid) DisplayList - explicit DisplayList() {} + explicit SkiaDisplayListWrapper() {} // Constructs a DisplayList from a SkiaDisplayList - explicit DisplayList(std::unique_ptr<skiapipeline::SkiaDisplayList> impl) + explicit SkiaDisplayListWrapper(std::unique_ptr<skiapipeline::SkiaDisplayList> impl) : mImpl(std::move(impl)) {} // Move support - DisplayList(DisplayList&& other) : mImpl(std::move(other.mImpl)) {} - DisplayList& operator=(DisplayList&& other) { + SkiaDisplayListWrapper(SkiaDisplayListWrapper&& other) : mImpl(std::move(other.mImpl)) {} + SkiaDisplayListWrapper& operator=(SkiaDisplayListWrapper&& other) { mImpl = std::move(other.mImpl); return *this; } // No copy support - DisplayList(const DisplayList& other) = delete; - DisplayList& operator=(const DisplayList&) = delete; + SkiaDisplayListWrapper(const SkiaDisplayListWrapper& other) = delete; + SkiaDisplayListWrapper& operator=(const SkiaDisplayListWrapper&) = delete; void updateChildren(std::function<void(RenderNode*)> updateFn) { mImpl->updateChildren(std::move(updateFn)); @@ -137,7 +135,7 @@ public: void applyColorTransform(ColorTransform transform) { if (mImpl) { - mImpl->mDisplayList.applyColorTransform(transform); + mImpl->applyColorTransform(transform); } } @@ -145,5 +143,172 @@ private: std::unique_ptr<skiapipeline::SkiaDisplayList> mImpl; }; + +/** + * Data structure that holds the list of commands used in display list stream + */ +//using DisplayList = skiapipeline::SkiaDisplayList; +class MultiDisplayList { +private: + using SkiaDisplayList = skiapipeline::SkiaDisplayList; + + struct EmptyList { + bool hasText() const { return false; } + void updateChildren(std::function<void(RenderNode*)> updateFn) {} + bool isEmpty() const { return true; } + bool containsProjectionReceiver() const { return false; } + bool hasVectorDrawables() const { return false; } + size_t getUsedSize() const { return 0; } + size_t getAllocatedSize() const { return 0; } + void output(std::ostream& output, uint32_t level) const { } + bool hasFunctor() const { return false; } + bool prepareListAndChildren( + TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, + std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) { + return false; + } + void syncContents(const WebViewSyncData& data) { } + void applyColorTransform(ColorTransform transform) { } + }; + + std::variant<EmptyList, std::unique_ptr<SkiaDisplayList>, CanvasOpBuffer> mImpls; + + template <typename T> + static constexpr T& get(T& t) { return t; } + template <typename T> + static constexpr const T& get(const T& t) { return t; } + + template <typename T> + static constexpr T& get(std::unique_ptr<T>& t) { return *t; } + template <typename T> + static constexpr const T& get(const std::unique_ptr<T>& t) { return *t; } + + template <typename T> + auto apply(T&& t) { + return std::visit([&t](auto& it) -> auto { + return t(get(it)); + }, mImpls); + } + + template <typename T> + auto apply(T&& t) const { + return std::visit([&t](const auto& it) -> auto { + return t(get(it)); + }, mImpls); + } + +public: + // Constructs an empty (invalid) DisplayList + explicit MultiDisplayList() {} + + // Constructs a DisplayList from a SkiaDisplayList + explicit MultiDisplayList(std::unique_ptr<SkiaDisplayList> impl) + : mImpls(std::move(impl)) {} + + explicit MultiDisplayList(CanvasOpBuffer&& opBuffer) : mImpls(std::move(opBuffer)) {} + + // Move support + MultiDisplayList(MultiDisplayList&& other) : mImpls(std::move(other.mImpls)) {} + MultiDisplayList& operator=(MultiDisplayList&& other) { + mImpls = std::move(other.mImpls); + return *this; + } + + // No copy support + MultiDisplayList(const MultiDisplayList& other) = delete; + MultiDisplayList& operator=(const MultiDisplayList&) = delete; + + void updateChildren(std::function<void(RenderNode*)> updateFn) { + apply([&](auto& it) { it.updateChildren(std::move(updateFn)); }); + } + + [[nodiscard]] explicit operator bool() const { + return isValid(); + } + + // If true this DisplayList contains a backing content, even if that content is empty + // If false, there this DisplayList is in an "empty" state + [[nodiscard]] bool isValid() const { + return mImpls.index() != 0; + } + + [[nodiscard]] bool isEmpty() const { + return apply([](const auto& it) -> auto { return it.isEmpty(); }); + } + + [[nodiscard]] bool hasContent() const { + return !isEmpty(); + } + + [[nodiscard]] bool containsProjectionReceiver() const { + return apply([](const auto& it) -> auto { return it.containsProjectionReceiver(); }); + } + + [[nodiscard]] SkiaDisplayList* asSkiaDl() { + return std::get<1>(mImpls).get(); + } + + [[nodiscard]] const SkiaDisplayList* asSkiaDl() const { + return std::get<1>(mImpls).get(); + } + + [[nodiscard]] bool hasVectorDrawables() const { + return apply([](const auto& it) -> auto { return it.hasVectorDrawables(); }); + } + + void clear(RenderNode* owningNode = nullptr) { + if (owningNode && mImpls.index() == 1) { + auto& skiaDl = std::get<1>(mImpls); + if (skiaDl->reuseDisplayList(owningNode)) { + skiaDl.release(); + } + } + mImpls = EmptyList{}; + } + + [[nodiscard]] size_t getUsedSize() const { + return apply([](const auto& it) -> auto { return it.getUsedSize(); }); + } + + [[nodiscard]] size_t getAllocatedSize() const { + return apply([](const auto& it) -> auto { return it.getAllocatedSize(); }); + } + + void output(std::ostream& output, uint32_t level) const { + apply([&](const auto& it) { it.output(output, level); }); + } + + [[nodiscard]] bool hasFunctor() const { + return apply([](const auto& it) -> auto { return it.hasFunctor(); }); + } + + bool prepareListAndChildren( + TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, + std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) { + return apply([&](auto& it) -> auto { + return it.prepareListAndChildren(observer, info, functorsNeedLayer, std::move(childFn)); + }); + } + + void syncContents(const WebViewSyncData& data) { + apply([&](auto& it) { it.syncContents(data); }); + } + + [[nodiscard]] bool hasText() const { + return apply([](const auto& it) -> auto { return it.hasText(); }); + } + + void applyColorTransform(ColorTransform transform) { + apply([=](auto& it) { it.applyColorTransform(transform); }); + } + + [[nodiscard]] CanvasOpBuffer& asOpBuffer() { + return std::get<CanvasOpBuffer>(mImpls); + } +}; + +// For now stick to the original single-type container to avoid any regressions +using DisplayList = SkiaDisplayListWrapper; + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/canvas/CanvasFrontend.h b/libs/hwui/canvas/CanvasFrontend.h index d749d2f2596b..f9a610129d3a 100644 --- a/libs/hwui/canvas/CanvasFrontend.h +++ b/libs/hwui/canvas/CanvasFrontend.h @@ -147,8 +147,7 @@ class CanvasFrontend final : public CanvasStateHelper { public: template<class... Args> CanvasFrontend(int width, int height, Args&&... args) : CanvasStateHelper(width, height), - mReceiver(std::forward<Args>(args)...) { } - ~CanvasFrontend() = default; + mReceiver(std::in_place, std::forward<Args>(args)...) { } void save(SaveFlags::Flags flags = SaveFlags::MatrixClip) { if (internalSave(flagsToSaveEntry(flags))) { @@ -186,7 +185,10 @@ public: submit(std::move(op)); } - const CanvasOpReceiver& receiver() const { return *mReceiver; } + const CanvasOpReceiver& receiver() const { + LOG_ALWAYS_FATAL_IF(!mReceiver.has_value()); + return *mReceiver; + } CanvasOpReceiver finish() { auto ret = std::move(mReceiver.value()); @@ -205,6 +207,7 @@ private: template <CanvasOpType T> void submit(CanvasOp<T>&& op) { + LOG_ALWAYS_FATAL_IF(!mReceiver.has_value()); mReceiver->push_container(CanvasOpContainer(std::move(op), transform())); } }; diff --git a/libs/hwui/canvas/CanvasOpBuffer.cpp b/libs/hwui/canvas/CanvasOpBuffer.cpp index 7054e47eac89..6089c572b0d5 100644 --- a/libs/hwui/canvas/CanvasOpBuffer.cpp +++ b/libs/hwui/canvas/CanvasOpBuffer.cpp @@ -22,4 +22,32 @@ namespace android::uirenderer { template class OpBuffer<CanvasOpType, CanvasOpContainer>; +void CanvasOpBuffer::updateChildren(std::function<void(RenderNode*)> updateFn) { + // TODO: Do we need a fast-path for finding children? + if (mHas.children) { + for (auto& iter : filter<CanvasOpType::DrawRenderNode>()) { + updateFn(iter->renderNode.get()); + } + } +} + +void CanvasOpBuffer::output(std::ostream& output, uint32_t level) const { + LOG_ALWAYS_FATAL("TODO"); +} + +bool CanvasOpBuffer::prepareListAndChildren( + TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, + std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) { + LOG_ALWAYS_FATAL("TODO"); + return false; +} + +void CanvasOpBuffer::syncContents(const WebViewSyncData& data) { + LOG_ALWAYS_FATAL("TODO"); +} + +void CanvasOpBuffer::applyColorTransform(ColorTransform transform) { + LOG_ALWAYS_FATAL("TODO"); +} + } // namespace android::uirenderer diff --git a/libs/hwui/canvas/CanvasOpBuffer.h b/libs/hwui/canvas/CanvasOpBuffer.h index 07e079a7d57f..af797ca8288b 100644 --- a/libs/hwui/canvas/CanvasOpBuffer.h +++ b/libs/hwui/canvas/CanvasOpBuffer.h @@ -19,10 +19,17 @@ #include <SkMatrix.h> #include "CanvasOpTypes.h" +#include "CanvasTransform.h" #include "OpBuffer.h" +#include "TreeInfo.h" +#include "private/hwui/WebViewFunctor.h" + +#include <functional> namespace android::uirenderer { +class RenderNode; + template <CanvasOpType T> struct CanvasOp; @@ -53,12 +60,74 @@ public: }; extern template class OpBuffer<CanvasOpType, CanvasOpContainer>; -class CanvasOpBuffer final : public OpBuffer<CanvasOpType, CanvasOpContainer> { +class CanvasOpBuffer final : private OpBuffer<CanvasOpType, CanvasOpContainer> { +private: + using SUPER = OpBuffer<CanvasOpType, CanvasOpContainer>; + public: + // Expose select superclass methods publicly + using SUPER::for_each; + using SUPER::size; + using SUPER::resize; + template <CanvasOpType T> void push(CanvasOp<T>&& op) { push_container(CanvasOpContainer<T>(std::move(op))); } + + template <CanvasOpType T> + void push_container(CanvasOpContainer<T>&& op) { + if constexpr (IsDrawOp(T)) { + mHas.content = true; + } + if constexpr (T == CanvasOpType::DrawRenderNode) { + mHas.children = true; + // use staging property, since recording on UI thread + if (op->renderNode->stagingProperties().isProjectionReceiver()) { + mHas.projectionReceiver = true; + } + } + SUPER::push_container(std::move(op)); + } + + void clear() { + mHas = Contains{}; + SUPER::clear(); + } + + void updateChildren(std::function<void(RenderNode*)> updateFn); + bool prepareListAndChildren( + TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer, + std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn); + void syncContents(const WebViewSyncData& data); + void applyColorTransform(ColorTransform transform); + + [[nodiscard]] bool isEmpty() const { return !mHas.content; } + [[nodiscard]] bool hasText() const { return mHas.text; } + [[nodiscard]] bool hasVectorDrawables() const { return mHas.vectorDrawable; } + [[nodiscard]] bool containsProjectionReceiver() const { return mHas.projectionReceiver; } + [[nodiscard]] bool hasFunctor() const { return mHas.functor; } + + [[nodiscard]] size_t getUsedSize() const { + return size(); + } + + [[nodiscard]] size_t getAllocatedSize() const { + return capacity(); + } + + void output(std::ostream& output, uint32_t level) const; + +private: + struct Contains { + bool content : 1 = false; + bool children : 1 = false; + bool projectionReceiver : 1 = false; + bool text : 1 = false; + bool vectorDrawable : 1 = false; + bool functor : 1 = false; + }; + Contains mHas; }; } // namespace android::uirenderer diff --git a/libs/hwui/canvas/CanvasOpRasterizer.cpp b/libs/hwui/canvas/CanvasOpRasterizer.cpp index 0093c38cf8a8..9297604197c0 100644 --- a/libs/hwui/canvas/CanvasOpRasterizer.cpp +++ b/libs/hwui/canvas/CanvasOpRasterizer.cpp @@ -33,21 +33,15 @@ void rasterizeCanvasBuffer(const CanvasOpBuffer& source, SkCanvas* destination) SkMatrix& currentGlobalTransform = globalMatrixStack.emplace_back(SkMatrix::I()); source.for_each([&]<CanvasOpType T>(const CanvasOpContainer<T> * op) { - if constexpr ( - T == CanvasOpType::BeginZ || - T == CanvasOpType::EndZ || - T == CanvasOpType::DrawLayer - ) { - // Do beginZ or endZ - LOG_ALWAYS_FATAL("TODO"); - return; - } else { + if constexpr (CanvasOpTraits::can_draw<CanvasOp<T>>) { // Generic OP // First apply the current transformation destination->setMatrix(SkMatrix::Concat(currentGlobalTransform, op->transform())); // Now draw it (*op)->draw(destination); + return; } + LOG_ALWAYS_FATAL("TODO, unable to rasterize %d", static_cast<int>(T)); }); } diff --git a/libs/hwui/canvas/CanvasOpTypes.h b/libs/hwui/canvas/CanvasOpTypes.h index cde50bd66a15..b55ef9d5537c 100644 --- a/libs/hwui/canvas/CanvasOpTypes.h +++ b/libs/hwui/canvas/CanvasOpTypes.h @@ -35,7 +35,8 @@ enum class CanvasOpType : int8_t { ClipPath, // Drawing ops - DrawColor, + DRAW_OP_BEGIN, + DrawColor = DRAW_OP_BEGIN, DrawRect, DrawRegion, DrawRoundRect, @@ -59,10 +60,16 @@ enum class CanvasOpType : int8_t { DrawImageLattice, DrawPicture, DrawLayer, + DrawRenderNode, + DRAW_OP_END = DrawRenderNode, // TODO: Rest COUNT // must be last }; +static constexpr bool IsDrawOp(CanvasOpType t) { + return CanvasOpType::DRAW_OP_BEGIN <= t && t <= CanvasOpType::DRAW_OP_END; +} + } // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h index 86b1ac71f692..855cd0d6436b 100644 --- a/libs/hwui/canvas/CanvasOps.h +++ b/libs/hwui/canvas/CanvasOps.h @@ -24,13 +24,15 @@ #include <SkImage.h> #include <SkPicture.h> #include <SkRuntimeEffect.h> -#include <hwui/Bitmap.h> + #include <log/log.h> -#include "CanvasProperty.h" -#include "Points.h" +#include "hwui/Bitmap.h" +#include "CanvasProperty.h" #include "CanvasOpTypes.h" #include "Layer.h" +#include "Points.h" +#include "RenderNode.h" #include <experimental/type_traits> #include <utility> @@ -450,6 +452,11 @@ struct CanvasOp<CanvasOpType::DrawLayer> { sp<Layer> layer; }; +template<> +struct CanvasOp<CanvasOpType::DrawRenderNode> { + sp<RenderNode> renderNode; +}; + // cleanup our macros #undef ASSERT_DRAWABLE diff --git a/libs/hwui/canvas/OpBuffer.h b/libs/hwui/canvas/OpBuffer.h index 1237d69a3c24..8b5cdbbec958 100644 --- a/libs/hwui/canvas/OpBuffer.h +++ b/libs/hwui/canvas/OpBuffer.h @@ -64,7 +64,7 @@ public: static constexpr auto STARTING_SIZE = PadAlign(sizeof(BufferHeader)); using ItemHeader = OpBufferItemHeader<ItemTypes>; - OpBuffer() = default; + explicit OpBuffer() = default; // Prevent copying by default OpBuffer(const OpBuffer&) = delete; @@ -135,7 +135,7 @@ public: template <typename F> void for_each(F&& f) const { - for_each(std::forward<F>(f), ItemTypesSequence{}); + do_for_each(std::forward<F>(f), ItemTypesSequence{}); } void clear(); @@ -225,7 +225,7 @@ private: } template <typename F, std::size_t... I> - void for_each(F&& f, std::index_sequence<I...>) const { + void do_for_each(F&& f, std::index_sequence<I...>) const { // Validate we're not empty if (isEmpty()) return; diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h index 483264f95e60..1136e58e43d8 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.h +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h @@ -16,6 +16,8 @@ #pragma once +#include <deque> + #include "RecordingCanvas.h" #include "RenderNodeDrawable.h" #include "TreeInfo.h" @@ -23,8 +25,6 @@ #include "utils/LinearAllocator.h" #include "utils/Pair.h" -#include <deque> - namespace android { namespace uirenderer { @@ -46,8 +46,10 @@ class FunctorDrawable; class SkiaDisplayList { public: - size_t getUsedSize() { return allocator.usedSize() + mDisplayList.usedSize(); } - size_t getAllocatedSize() { return allocator.allocatedSize() + mDisplayList.allocatedSize(); } + size_t getUsedSize() const { return allocator.usedSize() + mDisplayList.usedSize(); } + size_t getAllocatedSize() const { + return allocator.allocatedSize() + mDisplayList.allocatedSize(); + } ~SkiaDisplayList() { /* Given that we are using a LinearStdAllocator to store some of the @@ -109,6 +111,10 @@ public: */ void syncContents(const WebViewSyncData& data); + void applyColorTransform(ColorTransform transform) { + mDisplayList.applyColorTransform(transform); + } + /** * ONLY to be called by RenderNode::prepareTree in order to prepare this * list while the UI thread is blocked. Here we can upload mutable bitmaps @@ -154,12 +160,12 @@ public: std::deque<RenderNodeDrawable> mChildNodes; std::deque<FunctorDrawable*> mChildFunctors; std::vector<SkImage*> mMutableImages; + private: std::vector<Pair<VectorDrawableRoot*, SkMatrix>> mVectorDrawables; + public: - void appendVD(VectorDrawableRoot* r) { - appendVD(r, SkMatrix::I()); - } + void appendVD(VectorDrawableRoot* r) { appendVD(r, SkMatrix::I()); } void appendVD(VectorDrawableRoot* r, const SkMatrix& mat) { mVectorDrawables.push_back(Pair<VectorDrawableRoot*, SkMatrix>(r, mat)); diff --git a/libs/hwui/tests/unit/CanvasFrontendTests.cpp b/libs/hwui/tests/unit/CanvasFrontendTests.cpp index 05b11795d90d..4ddcf6f6ac8a 100644 --- a/libs/hwui/tests/unit/CanvasFrontendTests.cpp +++ b/libs/hwui/tests/unit/CanvasFrontendTests.cpp @@ -124,12 +124,12 @@ TEST(CanvasFrontend, transform) { TEST(CanvasFrontend, drawOpTransform) { CanvasFrontend<CanvasOpBuffer> opCanvas(100, 100); - const auto& receiver = opCanvas.receiver(); + const auto &receiver = opCanvas.receiver(); auto makeDrawRect = [] { return CanvasOp<CanvasOpType::DrawRect>{ - .rect = SkRect::MakeWH(50, 50), - .paint = SkPaint(SkColors::kBlack), + .rect = SkRect::MakeWH(50, 50), + .paint = SkPaint(SkColors::kBlack), }; }; @@ -167,14 +167,14 @@ TEST(CanvasFrontend, drawOpTransform) { { // First result should be identity - const auto& result = transforms[0]; + const auto &result = transforms[0]; EXPECT_EQ(SkMatrix::kIdentity_Mask, result.getType()); EXPECT_EQ(SkMatrix::I(), result); } { // Should be translate 10, 10 - const auto& result = transforms[1]; + const auto &result = transforms[1]; EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType()); SkMatrix m; m.setTranslate(10, 10); @@ -183,7 +183,7 @@ TEST(CanvasFrontend, drawOpTransform) { { // Should be translate 10, 10 + scale 2, 4 - const auto& result = transforms[2]; + const auto &result = transforms[2]; EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask, result.getType()); SkMatrix m; m.setTranslate(10, 10); @@ -193,7 +193,7 @@ TEST(CanvasFrontend, drawOpTransform) { { // Should be translate 10, 10 + translate 20, 15 - const auto& result = transforms[3]; + const auto &result = transforms[3]; EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType()); SkMatrix m; m.setTranslate(30, 25); @@ -202,9 +202,9 @@ TEST(CanvasFrontend, drawOpTransform) { { // Should be translate 10, 10 + translate 20, 15 + rotate 90 - const auto& result = transforms[4]; + const auto &result = transforms[4]; EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask, - result.getType()); + result.getType()); SkMatrix m; m.setTranslate(30, 25); m.preRotate(90.f); diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp index 54970df534b9..a718d46c735f 100644 --- a/libs/hwui/tests/unit/CanvasOpTests.cpp +++ b/libs/hwui/tests/unit/CanvasOpTests.cpp @@ -149,7 +149,7 @@ TEST(CanvasOp, simpleDrawPoints) { CanvasOpBuffer buffer; EXPECT_EQ(buffer.size(), 0); size_t numPts = 3; - auto pts = sk_ref_sp( + auto pts = sk_sp<Points>( new Points({ {32, 16}, {48, 48}, @@ -192,7 +192,7 @@ TEST(CanvasOp, simpleDrawLines) { CanvasOpBuffer buffer; EXPECT_EQ(buffer.size(), 0); size_t numPts = 3; - auto pts = sk_ref_sp( + auto pts = sk_sp<Points>( new Points({ {32, 16}, {48, 48}, diff --git a/libs/hwui/tests/unit/EglManagerTests.cpp b/libs/hwui/tests/unit/EglManagerTests.cpp new file mode 100644 index 000000000000..f7f240663397 --- /dev/null +++ b/libs/hwui/tests/unit/EglManagerTests.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 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 "renderthread/EglManager.h" +#include "tests/common/TestContext.h" + +using namespace android; +using namespace android::uirenderer; +using namespace android::uirenderer::renderthread; +using namespace android::uirenderer::test; + +TEST(EglManager, doesSurfaceLeak) { + EglManager eglManager; + eglManager.initialize(); + + ASSERT_TRUE(eglManager.hasEglContext()); + + auto colorSpace = SkColorSpace::MakeSRGB(); + for (int i = 0; i < 100; i++) { + TestContext context; + auto result = + eglManager.createSurface(context.surface().get(), ColorMode::Default, colorSpace); + EXPECT_TRUE(result); + EGLSurface surface = result.unwrap(); + eglManager.destroySurface(surface); + } + + eglManager.destroy(); +}
\ No newline at end of file diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index 6dd57b19a41b..8c999c41bf7b 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -404,6 +404,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) { EXPECT_TRUE(pipeline->isSurfaceReady()); renderThread.destroyRenderingContext(); EXPECT_FALSE(pipeline->isSurfaceReady()); + LOG_ALWAYS_FATAL_IF(pipeline->isSurfaceReady()); } RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) { |