diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java index 080ed9ae18418..2481e049933b5 100644 --- a/core/java/android/view/DisplayListCanvas.java +++ b/core/java/android/view/DisplayListCanvas.java @@ -163,17 +163,33 @@ public class DisplayListCanvas extends Canvas { /////////////////////////////////////////////////////////////////////////// /** - * Calls the function specified with the drawGLFunction function pointer. This is - * functionality used by webkit for calling into their renderer from our display lists. - * This function may return true if an invalidation is needed after the call. + * Records the functor specified with the drawGLFunction function pointer. This is + * functionality used by webview for calling into their renderer from our display lists. * * @param drawGLFunction A native function pointer */ public void callDrawGLFunction2(long drawGLFunction) { - nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunction); + nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunction, null); } - private static native void nCallDrawGLFunction(long renderer, long drawGLFunction); + /** + * Records the functor specified with the drawGLFunction function pointer. This is + * functionality used by webview for calling into their renderer from our display lists. + * + * @param drawGLFunction A native function pointer + * @param releasedCallback Called when the display list is destroyed, and thus + * the functor is no longer referenced by this canvas's display list. + * + * NOTE: The callback does *not* necessarily mean that there are no longer + * any references to the functor, just that the reference from this specific + * canvas's display list has been released. + */ + public void drawGLFunctor2(long drawGLFunctor, Runnable releasedCallback) { + nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunctor, releasedCallback); + } + + private static native void nCallDrawGLFunction(long renderer, + long drawGLFunction, Runnable releasedCallback); /////////////////////////////////////////////////////////////////////////// // Display list diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp index 6aac0e4975e8c..cadfd3d89a2a0 100644 --- a/core/jni/android_view_DisplayListCanvas.cpp +++ b/core/jni/android_view_DisplayListCanvas.cpp @@ -22,12 +22,12 @@ #include +#include #include #include #include - #include #include #include @@ -41,6 +41,52 @@ namespace android { using namespace uirenderer; +jmethodID gRunnableMethodId; + +static JNIEnv* jnienv(JavaVM* vm) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm); + } + return env; +} + +class InvokeRunnableMessage : public MessageHandler { +public: + InvokeRunnableMessage(JNIEnv* env, jobject runnable) { + mRunnable = env->NewGlobalRef(runnable); + env->GetJavaVM(&mVm); + } + + virtual ~InvokeRunnableMessage() { + jnienv(mVm)->DeleteGlobalRef(mRunnable); + } + + virtual void handleMessage(const Message&) { + jnienv(mVm)->CallVoidMethod(mRunnable, gRunnableMethodId); + } + +private: + JavaVM* mVm; + jobject mRunnable; +}; + +class GlFunctorReleasedCallbackBridge : public GlFunctorLifecycleListener { +public: + GlFunctorReleasedCallbackBridge(JNIEnv* env, jobject javaCallback) { + mLooper = Looper::getForThread(); + mMessage = new InvokeRunnableMessage(env, javaCallback); + } + + virtual void onGlFunctorReleased(Functor* functor) override { + mLooper->sendMessage(mMessage, 0); + } + +private: + sp mLooper; + sp mMessage; +}; + // ---------------------------------------------------------------------------- // Setup // ---------------------------------------------------------------------------- @@ -56,10 +102,12 @@ static void android_view_DisplayListCanvas_insertReorderBarrier(JNIEnv* env, job // ---------------------------------------------------------------------------- static void android_view_DisplayListCanvas_callDrawGLFunction(JNIEnv* env, jobject clazz, - jlong canvasPtr, jlong functorPtr) { + jlong canvasPtr, jlong functorPtr, jobject releasedCallback) { Canvas* canvas = reinterpret_cast(canvasPtr); Functor* functor = reinterpret_cast(functorPtr); - canvas->callDrawGLFunction(functor); + sp bridge(new GlFunctorReleasedCallbackBridge( + env, releasedCallback)); + canvas->callDrawGLFunction(functor, bridge.get()); } // ---------------------------------------------------------------------------- @@ -184,7 +232,8 @@ static JNINativeMethod gMethods[] = { { "nIsAvailable", "!()Z", (void*) android_view_DisplayListCanvas_isAvailable }, { "nInsertReorderBarrier","!(JZ)V", (void*) android_view_DisplayListCanvas_insertReorderBarrier }, - { "nCallDrawGLFunction", "!(JJ)V", (void*) android_view_DisplayListCanvas_callDrawGLFunction }, + { "nCallDrawGLFunction", "!(JJLjava/lang/Runnable;)V", + (void*) android_view_DisplayListCanvas_callDrawGLFunction }, { "nDrawRoundRect", "!(JJJJJJJJ)V", (void*) android_view_DisplayListCanvas_drawRoundRectProps }, { "nDrawCircle", "!(JJJJJ)V", (void*) android_view_DisplayListCanvas_drawCircleProps }, @@ -207,6 +256,9 @@ static JNINativeMethod gActivityThreadMethods[] = { }; int register_android_view_DisplayListCanvas(JNIEnv* env) { + jclass runnableClass = FindClassOrDie(env, "java/lang/Runnable"); + gRunnableMethodId = GetMethodIDOrDie(env, runnableClass, "run", "()V"); + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp index 181b3433381c6..b572bdaccb860 100644 --- a/libs/hwui/DisplayList.cpp +++ b/libs/hwui/DisplayList.cpp @@ -73,6 +73,12 @@ void DisplayList::cleanupResources() { delete path; } + for (auto& iter : functors) { + if (iter.listener) { + iter.listener->onGlFunctorReleased(iter.functor); + } + } + patchResources.clear(); pathResources.clear(); paints.clear(); diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index aba5d4bd218d3..5b3227b7db97d 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -35,6 +35,7 @@ #include "Debug.h" #include "CanvasProperty.h" #include "DeferredDisplayList.h" +#include "GlFunctorLifecycleListener.h" #include "Matrix.h" #include "RenderProperties.h" @@ -119,6 +120,11 @@ struct PushStagingFunctor { virtual void operator ()() {} }; +struct FunctorContainer { + Functor* functor; + GlFunctorLifecycleListener* listener; +}; + /** * Data structure that holds the list of commands used in display list stream */ @@ -154,7 +160,7 @@ public: const LsaVector& getChildren() const { return children; } const LsaVector& getBitmapResources() const { return bitmapResources; } - const LsaVector& getFunctors() const { return functors; } + const LsaVector& getFunctors() const { return functors; } const LsaVector& getPushStagingFunctors() { return pushStagingFunctors; } size_t addChild(NodeOpType* childOp); @@ -195,7 +201,7 @@ private: LsaVector< sp > referenceHolders; // List of functors - LsaVector functors; + LsaVector functors; // List of functors that need to be notified of pushStaging. Note that this list gets nothing // but a callback during sync DisplayList, unlike the list of functors defined above, which diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp index c6e92abbe0c36..ca968cef91b2a 100644 --- a/libs/hwui/DisplayListCanvas.cpp +++ b/libs/hwui/DisplayListCanvas.cpp @@ -81,9 +81,11 @@ DisplayList* DisplayListCanvas::finishRecording() { return displayList; } -void DisplayListCanvas::callDrawGLFunction(Functor *functor) { +void DisplayListCanvas::callDrawGLFunction(Functor* functor, + GlFunctorLifecycleListener* listener) { addDrawOp(new (alloc()) DrawFunctorOp(functor)); - mDisplayList->functors.push_back(functor); + mDisplayList->functors.push_back({functor, listener}); + mDisplayList->ref(listener); } SkCanvas* DisplayListCanvas::asSkCanvas() { diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h index d6a5794734d65..664f79e283b61 100644 --- a/libs/hwui/DisplayListCanvas.h +++ b/libs/hwui/DisplayListCanvas.h @@ -93,7 +93,8 @@ public: // ---------------------------------------------------------------------------- virtual void drawLayer(DeferredLayerUpdater* layerHandle) override; virtual void drawRenderNode(RenderNode* renderNode) override; - virtual void callDrawGLFunction(Functor* functor) override; + virtual void callDrawGLFunction(Functor* functor, + GlFunctorLifecycleListener* listener) override; // ---------------------------------------------------------------------------- // CanvasStateClient interface diff --git a/libs/hwui/GlFunctorLifecycleListener.h b/libs/hwui/GlFunctorLifecycleListener.h new file mode 100644 index 0000000000000..357090eb31ca7 --- /dev/null +++ b/libs/hwui/GlFunctorLifecycleListener.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 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 +#include + +namespace android { +namespace uirenderer { + +class GlFunctorLifecycleListener : public VirtualLightRefBase { +public: + virtual ~GlFunctorLifecycleListener() {} + virtual void onGlFunctorReleased(Functor* functor) = 0; +}; + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index ab733f138b258..b49f9b5299894 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -594,8 +594,10 @@ void RecordingCanvas::drawLayer(DeferredLayerUpdater* layerHandle) { layerHandle->backingLayer())); } -void RecordingCanvas::callDrawGLFunction(Functor* functor) { - mDisplayList->functors.push_back(functor); +void RecordingCanvas::callDrawGLFunction(Functor* functor, + GlFunctorLifecycleListener* listener) { + mDisplayList->functors.push_back({functor, listener}); + mDisplayList->ref(listener); addOp(alloc().create_trivial( *(mState.currentSnapshot()->transform), getRecordedClip(), diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 219296c97bb65..372be241042a3 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -59,7 +59,8 @@ public: virtual void drawLayer(DeferredLayerUpdater* layerHandle) override; virtual void drawRenderNode(RenderNode* renderNode) override; - virtual void callDrawGLFunction(Functor* functor) override; + virtual void callDrawGLFunction(Functor* functor, + GlFunctorLifecycleListener* listener) override; // ---------------------------------------------------------------------------- // CanvasStateClient interface diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index ea06fcd092ce4..6e848fddf48f6 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -474,8 +474,8 @@ void RenderNode::syncDisplayList(TreeObserver* observer) { mDisplayList = mStagingDisplayList; mStagingDisplayList = nullptr; if (mDisplayList) { - for (size_t i = 0; i < mDisplayList->getFunctors().size(); i++) { - (*mDisplayList->getFunctors()[i])(DrawGlInfo::kModeSync, nullptr); + for (auto& iter : mDisplayList->getFunctors()) { + (*iter.functor)(DrawGlInfo::kModeSync, nullptr); } for (size_t i = 0; i < mDisplayList->getPushStagingFunctors().size(); i++) { (*mDisplayList->getPushStagingFunctors()[i])(); diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 1b459c1425610..ce67554645d13 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -160,7 +160,8 @@ public: virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override; virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override; - virtual void callDrawGLFunction(Functor* functor) override; + virtual void callDrawGLFunction(Functor* functor, + uirenderer::GlFunctorLifecycleListener* listener) override; protected: virtual void drawGlyphs(const uint16_t* text, const float* positions, int count, @@ -846,6 +847,7 @@ void SkiaCanvas::drawLayer(uirenderer::DeferredLayerUpdater* layer) { } void SkiaCanvas::drawRenderNode(uirenderer::RenderNode* renderNode) { } -void SkiaCanvas::callDrawGLFunction(Functor* functor) { } +void SkiaCanvas::callDrawGLFunction(Functor* functor, + uirenderer::GlFunctorLifecycleListener* listener) { } } // namespace android diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 5dbda43cec47d..55af33e802560 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -20,6 +20,7 @@ #include #include +#include "GlFunctorLifecycleListener.h" #include "utils/NinePatch.h" #include @@ -124,7 +125,8 @@ public: virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) = 0; virtual void drawRenderNode(uirenderer::RenderNode* renderNode) = 0; - virtual void callDrawGLFunction(Functor* functor) = 0; + virtual void callDrawGLFunction(Functor* functor, + uirenderer::GlFunctorLifecycleListener* listener) = 0; // ---------------------------------------------------------------------------- // Canvas state operations diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp index 209a10483a8ef..ebc1c8002304e 100644 --- a/libs/hwui/tests/unit/FrameBuilderTests.cpp +++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp @@ -581,7 +581,7 @@ RENDERTHREAD_TEST(FrameBuilder, functor_reject) { auto scrolledFunctorView = TestUtils::createNode(0, 0, 400, 1000000, [&noopFunctor](RenderProperties& props, RecordingCanvas& canvas) { canvas.translate(0, -800000); - canvas.callDrawGLFunction(&noopFunctor); + canvas.callDrawGLFunction(&noopFunctor, nullptr); }); FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp index 7c57a50c951d3..b2997dfb357fc 100644 --- a/libs/hwui/tests/unit/RenderNodeTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeTests.cpp @@ -51,3 +51,41 @@ TEST(RenderNode, hasParents) { EXPECT_FALSE(child->hasParents()) << "Child should be removed"; EXPECT_FALSE(parent->hasParents()) << "Root node shouldn't have any parents"; } + +TEST(RenderNode, releasedCallback) { + class DecRefOnReleased : public GlFunctorLifecycleListener { + public: + DecRefOnReleased(int* refcnt) : mRefCnt(refcnt) {} + void onGlFunctorReleased(Functor* functor) override { + *mRefCnt -= 1; + } + private: + int* mRefCnt; + }; + + int refcnt = 0; + sp listener(new DecRefOnReleased(&refcnt)); + Functor noopFunctor; + + auto node = TestUtils::createNode(0, 0, 200, 400, + [&](RenderProperties& props, TestCanvas& canvas) { + refcnt++; + canvas.callDrawGLFunction(&noopFunctor, listener.get()); + }); + TestUtils::syncHierarchyPropertiesAndDisplayList(node); + EXPECT_EQ(1, refcnt); + + TestUtils::recordNode(*node, [&](TestCanvas& canvas) { + refcnt++; + canvas.callDrawGLFunction(&noopFunctor, listener.get()); + }); + EXPECT_EQ(2, refcnt); + + TestUtils::syncHierarchyPropertiesAndDisplayList(node); + EXPECT_EQ(1, refcnt); + + TestUtils::recordNode(*node, [](TestCanvas& canvas) {}); + EXPECT_EQ(1, refcnt); + TestUtils::syncHierarchyPropertiesAndDisplayList(node); + EXPECT_EQ(0, refcnt); +}