Loading libs/hwui/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -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", Loading libs/hwui/DisplayList.h +177 −12 Original line number Diff line number Diff line Loading @@ -17,8 +17,10 @@ #pragma once #include "pipeline/skia/SkiaDisplayList.h" #include "canvas/CanvasOpBuffer.h" #include <memory> #include <variant> namespace android { namespace uirenderer { Loading @@ -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)); Loading Loading @@ -137,7 +135,7 @@ public: void applyColorTransform(ColorTransform transform) { if (mImpl) { mImpl->mDisplayList.applyColorTransform(transform); mImpl->applyColorTransform(transform); } } Loading @@ -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 libs/hwui/canvas/CanvasFrontend.h +6 −3 Original line number Diff line number Diff line Loading @@ -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))) { Loading Loading @@ -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()); Loading @@ -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())); } }; Loading libs/hwui/canvas/CanvasOpBuffer.cpp +28 −0 Original line number Diff line number Diff line Loading @@ -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 libs/hwui/canvas/CanvasOpBuffer.h +70 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading
libs/hwui/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -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", Loading
libs/hwui/DisplayList.h +177 −12 Original line number Diff line number Diff line Loading @@ -17,8 +17,10 @@ #pragma once #include "pipeline/skia/SkiaDisplayList.h" #include "canvas/CanvasOpBuffer.h" #include <memory> #include <variant> namespace android { namespace uirenderer { Loading @@ -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)); Loading Loading @@ -137,7 +135,7 @@ public: void applyColorTransform(ColorTransform transform) { if (mImpl) { mImpl->mDisplayList.applyColorTransform(transform); mImpl->applyColorTransform(transform); } } Loading @@ -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
libs/hwui/canvas/CanvasFrontend.h +6 −3 Original line number Diff line number Diff line Loading @@ -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))) { Loading Loading @@ -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()); Loading @@ -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())); } }; Loading
libs/hwui/canvas/CanvasOpBuffer.cpp +28 −0 Original line number Diff line number Diff line Loading @@ -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
libs/hwui/canvas/CanvasOpBuffer.h +70 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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