Add a callback for when a gl functor is released

Bug: 27709981

Change-Id: Id5be3e8f88d6d84a9c59c7ed23e7e8862feefbe8
This commit is contained in:
John Reck
2016-04-14 10:38:54 -07:00
parent a4c0b1de1a
commit cd1c3eba69
14 changed files with 183 additions and 23 deletions

View File

@@ -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

View File

@@ -22,12 +22,12 @@
#include <android_runtime/AndroidRuntime.h>
#include <utils/Looper.h>
#include <cutils/properties.h>
#include <SkBitmap.h>
#include <SkRegion.h>
#include <Rect.h>
#include <RenderNode.h>
#include <CanvasProperty.h>
@@ -41,6 +41,52 @@ namespace android {
using namespace uirenderer;
jmethodID gRunnableMethodId;
static JNIEnv* jnienv(JavaVM* vm) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&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<Looper> mLooper;
sp<InvokeRunnableMessage> 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<Canvas*>(canvasPtr);
Functor* functor = reinterpret_cast<Functor*>(functorPtr);
canvas->callDrawGLFunction(functor);
sp<GlFunctorReleasedCallbackBridge> 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));
}

View File

@@ -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();

View File

@@ -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<NodeOpType*>& getChildren() const { return children; }
const LsaVector<const SkBitmap*>& getBitmapResources() const { return bitmapResources; }
const LsaVector<Functor*>& getFunctors() const { return functors; }
const LsaVector<FunctorContainer>& getFunctors() const { return functors; }
const LsaVector<PushStagingFunctor*>& getPushStagingFunctors() { return pushStagingFunctors; }
size_t addChild(NodeOpType* childOp);
@@ -195,7 +201,7 @@ private:
LsaVector< sp<VirtualLightRefBase> > referenceHolders;
// List of functors
LsaVector<Functor*> functors;
LsaVector<FunctorContainer> 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

View File

@@ -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() {

View File

@@ -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

View File

@@ -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 <utils/Functor.h>
#include <utils/RefBase.h>
namespace android {
namespace uirenderer {
class GlFunctorLifecycleListener : public VirtualLightRefBase {
public:
virtual ~GlFunctorLifecycleListener() {}
virtual void onGlFunctorReleased(Functor* functor) = 0;
};
}; // namespace uirenderer
}; // namespace android

View File

@@ -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<FunctorOp>(
*(mState.currentSnapshot()->transform),
getRecordedClip(),

View File

@@ -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

View File

@@ -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])();

View File

@@ -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

View File

@@ -20,6 +20,7 @@
#include <cutils/compiler.h>
#include <utils/Functor.h>
#include "GlFunctorLifecycleListener.h"
#include "utils/NinePatch.h"
#include <SkBitmap.h>
@@ -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

View File

@@ -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,

View File

@@ -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<DecRefOnReleased> 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);
}