Loading libs/hwui/renderthread/CacheManager.h +2 −1 Original line number Diff line number Diff line Loading @@ -64,12 +64,13 @@ public: void unregisterCanvasContext(CanvasContext* context); void onContextStopped(CanvasContext* context); bool areAllContextsStopped(); private: friend class RenderThread; explicit CacheManager(RenderThread& thread); void setupCacheLimits(); bool areAllContextsStopped(); void checkUiHidden(); void scheduleDestroyContext(); void cancelDestroyContext(); Loading libs/hwui/renderthread/CanvasContext.cpp +6 −0 Original line number Diff line number Diff line Loading @@ -125,6 +125,7 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* , mRenderPipeline(std::move(renderPipeline)) , mHintSessionWrapper(uiThreadId, renderThreadId) { mRenderThread.cacheManager().registerCanvasContext(this); mRenderThread.renderState().registerContextCallback(this); rootRenderNode->makeRoot(); mRenderNodes.emplace_back(rootRenderNode); mProfiler.setDensity(DeviceInfo::getDensity()); Loading @@ -137,6 +138,7 @@ CanvasContext::~CanvasContext() { } mRenderNodes.clear(); mRenderThread.cacheManager().unregisterCanvasContext(this); mRenderThread.renderState().removeContextCallback(this); } void CanvasContext::addRenderNode(RenderNode* node, bool placeFront) { Loading Loading @@ -963,6 +965,10 @@ void CanvasContext::destroyHardwareResources() { } } void CanvasContext::onContextDestroyed() { destroyHardwareResources(); } DeferredLayerUpdater* CanvasContext::createTextureLayer() { return mRenderPipeline->createTextureLayer(); } Loading libs/hwui/renderthread/CanvasContext.h +3 −1 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ #include "Lighting.h" #include "ReliableSurface.h" #include "RenderNode.h" #include "renderstate/RenderState.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" #include "utils/RingBuffer.h" Loading @@ -64,7 +65,7 @@ class Frame; // This per-renderer class manages the bridge between the global EGL context // and the render surface. // TODO: Rename to Renderer or some other per-window, top-level manager class CanvasContext : public IFrameCallback { class CanvasContext : public IFrameCallback, public IGpuContextCallback { public: static CanvasContext* create(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory, pid_t uiThreadId, Loading Loading @@ -154,6 +155,7 @@ public: void markLayerInUse(RenderNode* node); void destroyHardwareResources(); void onContextDestroyed() override; DeferredLayerUpdater* createTextureLayer(); Loading libs/hwui/tests/common/TestUtils.h +25 −4 Original line number Diff line number Diff line Loading @@ -307,13 +307,21 @@ public: int destroyed = 0; int removeOverlays = 0; int glesDraw = 0; int vkInitialize = 0; int vkDraw = 0; int vkPostDraw = 0; }; static void expectOnRenderThread(const std::string_view& function = "unknown") { EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()) << "Called on wrong thread: " << function; } static WebViewFunctorCallbacks createMockFunctor(RenderMode mode) { static int createMockFunctor() { const auto renderMode = WebViewFunctor_queryPlatformRenderMode(); return WebViewFunctor_create(nullptr, createMockFunctorCallbacks(renderMode), renderMode); } static WebViewFunctorCallbacks createMockFunctorCallbacks(RenderMode mode) { auto callbacks = WebViewFunctorCallbacks{ .onSync = [](int functor, void* client_data, const WebViewSyncData& data) { Loading Loading @@ -345,9 +353,22 @@ public: sMockFunctorCounts[functor].glesDraw++; }; break; default: ADD_FAILURE(); return WebViewFunctorCallbacks{}; case RenderMode::Vulkan: callbacks.vk.initialize = [](int functor, void* data, const VkFunctorInitParams& params) { expectOnRenderThread("initialize"); sMockFunctorCounts[functor].vkInitialize++; }; callbacks.vk.draw = [](int functor, void* data, const VkFunctorDrawParams& params, const WebViewOverlayData& overlayParams) { expectOnRenderThread("draw"); sMockFunctorCounts[functor].vkDraw++; }; callbacks.vk.postDraw = [](int functor, void* data) { expectOnRenderThread("postDraw"); sMockFunctorCounts[functor].vkPostDraw++; }; break; } return callbacks; } Loading libs/hwui/tests/unit/CanvasContextTests.cpp +36 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include "AnimationContext.h" #include "IContextFactory.h" #include "renderthread/CanvasContext.h" #include "renderthread/VulkanManager.h" #include "tests/common/TestUtils.h" using namespace android; Loading @@ -42,3 +43,38 @@ RENDERTHREAD_TEST(CanvasContext, create) { canvasContext->destroy(); } RENDERTHREAD_TEST(CanvasContext, buildLayerDoesntLeak) { auto node = TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, Canvas& canvas) { canvas.drawColor(0xFFFF0000, SkBlendMode::kSrc); }); ASSERT_TRUE(node->isValid()); EXPECT_EQ(LayerType::None, node->stagingProperties().effectiveLayerType()); node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer); auto& cacheManager = renderThread.cacheManager(); EXPECT_TRUE(cacheManager.areAllContextsStopped()); ContextFactory contextFactory; std::unique_ptr<CanvasContext> canvasContext( CanvasContext::create(renderThread, false, node.get(), &contextFactory, 0, 0)); canvasContext->buildLayer(node.get()); EXPECT_TRUE(node->hasLayer()); if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { auto instance = VulkanManager::peekInstance(); if (instance) { EXPECT_TRUE(instance->hasVkContext()); } else { ADD_FAILURE() << "VulkanManager wasn't initialized to buildLayer?"; } } renderThread.destroyRenderingContext(); EXPECT_FALSE(node->hasLayer()) << "Node still has a layer after rendering context destroyed"; if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { auto instance = VulkanManager::peekInstance(); if (instance) { ADD_FAILURE() << "VulkanManager still exists"; EXPECT_FALSE(instance->hasVkContext()); } } } Loading
libs/hwui/renderthread/CacheManager.h +2 −1 Original line number Diff line number Diff line Loading @@ -64,12 +64,13 @@ public: void unregisterCanvasContext(CanvasContext* context); void onContextStopped(CanvasContext* context); bool areAllContextsStopped(); private: friend class RenderThread; explicit CacheManager(RenderThread& thread); void setupCacheLimits(); bool areAllContextsStopped(); void checkUiHidden(); void scheduleDestroyContext(); void cancelDestroyContext(); Loading
libs/hwui/renderthread/CanvasContext.cpp +6 −0 Original line number Diff line number Diff line Loading @@ -125,6 +125,7 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* , mRenderPipeline(std::move(renderPipeline)) , mHintSessionWrapper(uiThreadId, renderThreadId) { mRenderThread.cacheManager().registerCanvasContext(this); mRenderThread.renderState().registerContextCallback(this); rootRenderNode->makeRoot(); mRenderNodes.emplace_back(rootRenderNode); mProfiler.setDensity(DeviceInfo::getDensity()); Loading @@ -137,6 +138,7 @@ CanvasContext::~CanvasContext() { } mRenderNodes.clear(); mRenderThread.cacheManager().unregisterCanvasContext(this); mRenderThread.renderState().removeContextCallback(this); } void CanvasContext::addRenderNode(RenderNode* node, bool placeFront) { Loading Loading @@ -963,6 +965,10 @@ void CanvasContext::destroyHardwareResources() { } } void CanvasContext::onContextDestroyed() { destroyHardwareResources(); } DeferredLayerUpdater* CanvasContext::createTextureLayer() { return mRenderPipeline->createTextureLayer(); } Loading
libs/hwui/renderthread/CanvasContext.h +3 −1 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ #include "Lighting.h" #include "ReliableSurface.h" #include "RenderNode.h" #include "renderstate/RenderState.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" #include "utils/RingBuffer.h" Loading @@ -64,7 +65,7 @@ class Frame; // This per-renderer class manages the bridge between the global EGL context // and the render surface. // TODO: Rename to Renderer or some other per-window, top-level manager class CanvasContext : public IFrameCallback { class CanvasContext : public IFrameCallback, public IGpuContextCallback { public: static CanvasContext* create(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory, pid_t uiThreadId, Loading Loading @@ -154,6 +155,7 @@ public: void markLayerInUse(RenderNode* node); void destroyHardwareResources(); void onContextDestroyed() override; DeferredLayerUpdater* createTextureLayer(); Loading
libs/hwui/tests/common/TestUtils.h +25 −4 Original line number Diff line number Diff line Loading @@ -307,13 +307,21 @@ public: int destroyed = 0; int removeOverlays = 0; int glesDraw = 0; int vkInitialize = 0; int vkDraw = 0; int vkPostDraw = 0; }; static void expectOnRenderThread(const std::string_view& function = "unknown") { EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()) << "Called on wrong thread: " << function; } static WebViewFunctorCallbacks createMockFunctor(RenderMode mode) { static int createMockFunctor() { const auto renderMode = WebViewFunctor_queryPlatformRenderMode(); return WebViewFunctor_create(nullptr, createMockFunctorCallbacks(renderMode), renderMode); } static WebViewFunctorCallbacks createMockFunctorCallbacks(RenderMode mode) { auto callbacks = WebViewFunctorCallbacks{ .onSync = [](int functor, void* client_data, const WebViewSyncData& data) { Loading Loading @@ -345,9 +353,22 @@ public: sMockFunctorCounts[functor].glesDraw++; }; break; default: ADD_FAILURE(); return WebViewFunctorCallbacks{}; case RenderMode::Vulkan: callbacks.vk.initialize = [](int functor, void* data, const VkFunctorInitParams& params) { expectOnRenderThread("initialize"); sMockFunctorCounts[functor].vkInitialize++; }; callbacks.vk.draw = [](int functor, void* data, const VkFunctorDrawParams& params, const WebViewOverlayData& overlayParams) { expectOnRenderThread("draw"); sMockFunctorCounts[functor].vkDraw++; }; callbacks.vk.postDraw = [](int functor, void* data) { expectOnRenderThread("postDraw"); sMockFunctorCounts[functor].vkPostDraw++; }; break; } return callbacks; } Loading
libs/hwui/tests/unit/CanvasContextTests.cpp +36 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include "AnimationContext.h" #include "IContextFactory.h" #include "renderthread/CanvasContext.h" #include "renderthread/VulkanManager.h" #include "tests/common/TestUtils.h" using namespace android; Loading @@ -42,3 +43,38 @@ RENDERTHREAD_TEST(CanvasContext, create) { canvasContext->destroy(); } RENDERTHREAD_TEST(CanvasContext, buildLayerDoesntLeak) { auto node = TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, Canvas& canvas) { canvas.drawColor(0xFFFF0000, SkBlendMode::kSrc); }); ASSERT_TRUE(node->isValid()); EXPECT_EQ(LayerType::None, node->stagingProperties().effectiveLayerType()); node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer); auto& cacheManager = renderThread.cacheManager(); EXPECT_TRUE(cacheManager.areAllContextsStopped()); ContextFactory contextFactory; std::unique_ptr<CanvasContext> canvasContext( CanvasContext::create(renderThread, false, node.get(), &contextFactory, 0, 0)); canvasContext->buildLayer(node.get()); EXPECT_TRUE(node->hasLayer()); if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { auto instance = VulkanManager::peekInstance(); if (instance) { EXPECT_TRUE(instance->hasVkContext()); } else { ADD_FAILURE() << "VulkanManager wasn't initialized to buildLayer?"; } } renderThread.destroyRenderingContext(); EXPECT_FALSE(node->hasLayer()) << "Node still has a layer after rendering context destroyed"; if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { auto instance = VulkanManager::peekInstance(); if (instance) { ADD_FAILURE() << "VulkanManager still exists"; EXPECT_FALSE(instance->hasVkContext()); } } }