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

Commit 20a4d683 authored by John Reck's avatar John Reck
Browse files

Destroy prefetched layers when the context is destroyed

Also fix a few places preventing the tests from running
in SkiaVulkan, although that is still not enabled in general yet

Fixes: 283315634
Test: CanvasContext_buildLayerDoesntLeak unit test
Change-Id: I4d8a50db447e47afd8e8a267df332a173c88e888
parent 1fb0ec1d
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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();
+6 −0
Original line number Diff line number Diff line
@@ -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());
@@ -137,6 +138,7 @@ CanvasContext::~CanvasContext() {
    }
    mRenderNodes.clear();
    mRenderThread.cacheManager().unregisterCanvasContext(this);
    mRenderThread.renderState().removeContextCallback(this);
}

void CanvasContext::addRenderNode(RenderNode* node, bool placeFront) {
@@ -963,6 +965,10 @@ void CanvasContext::destroyHardwareResources() {
    }
}

void CanvasContext::onContextDestroyed() {
    destroyHardwareResources();
}

DeferredLayerUpdater* CanvasContext::createTextureLayer() {
    return mRenderPipeline->createTextureLayer();
}
+3 −1
Original line number Diff line number Diff line
@@ -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"
@@ -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,
@@ -154,6 +155,7 @@ public:
    void markLayerInUse(RenderNode* node);

    void destroyHardwareResources();
    void onContextDestroyed() override;

    DeferredLayerUpdater* createTextureLayer();

+25 −4
Original line number Diff line number Diff line
@@ -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) {
@@ -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;
    }
+36 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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