Merge "Add a callback for rendernode parentcount=0" into nyc-dev
am: bbb4c2e
* commit 'bbb4c2eb2e871977289a9c48f414962e46b56ddc':
Add a callback for rendernode parentcount=0
Change-Id: I8168b028ce61413da8ecea76e6531c5dde9ddb46
This commit is contained in:
@@ -763,6 +763,16 @@ public class RenderNode {
|
||||
return nGetDebugSize(mNativeRenderNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by native when the passed displaylist is removed from the draw tree
|
||||
*/
|
||||
void onRenderNodeDetached() {
|
||||
discardDisplayList();
|
||||
if (mOwningView != null) {
|
||||
mOwningView.onRenderNodeDetached(this);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Animations
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@@ -795,7 +805,9 @@ public class RenderNode {
|
||||
// Native methods
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private static native long nCreate(String name);
|
||||
// Intentionally not static because it acquires a reference to 'this'
|
||||
private native long nCreate(String name);
|
||||
|
||||
private static native void nDestroyRenderNode(long renderNode);
|
||||
private static native void nSetDisplayList(long renderNode, long newData);
|
||||
|
||||
|
||||
@@ -794,7 +794,8 @@ public final class ThreadedRenderer {
|
||||
}
|
||||
|
||||
final long[] frameInfo = choreographer.mFrameInfo.mFrameInfo;
|
||||
int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length);
|
||||
int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length,
|
||||
mRootNode.mNativeRenderNode);
|
||||
if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
|
||||
setEnabled(false);
|
||||
attachInfo.mViewRootImpl.mSurface.release();
|
||||
@@ -993,7 +994,8 @@ public final class ThreadedRenderer {
|
||||
private static native void nSetLightCenter(long nativeProxy,
|
||||
float lightX, float lightY, float lightZ);
|
||||
private static native void nSetOpaque(long nativeProxy, boolean opaque);
|
||||
private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
|
||||
private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size,
|
||||
long rootRenderNode);
|
||||
private static native void nDestroy(long nativeProxy, long rootRenderNode);
|
||||
private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
|
||||
|
||||
|
||||
@@ -16008,6 +16008,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the passed RenderNode is removed from the draw tree
|
||||
* @hide
|
||||
*/
|
||||
public void onRenderNodeDetached(RenderNode renderNode) {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Calling this method is equivalent to calling <code>getDrawingCache(false)</code>.</p>
|
||||
*
|
||||
|
||||
@@ -774,7 +774,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
* has invoked. If false, the functor may be invoked
|
||||
* asynchronously.
|
||||
*/
|
||||
public void invokeFunctor(long functor, boolean waitForCompletion) {
|
||||
public static void invokeFunctor(long functor, boolean waitForCompletion) {
|
||||
ThreadedRenderer.invokeFunctor(functor, waitForCompletion);
|
||||
}
|
||||
|
||||
|
||||
@@ -86,8 +86,7 @@ public final class WebViewDelegate {
|
||||
*/
|
||||
public void invokeDrawGlFunctor(View containerView, long nativeDrawGLFunctor,
|
||||
boolean waitForCompletion) {
|
||||
ViewRootImpl viewRootImpl = containerView.getViewRootImpl();
|
||||
viewRootImpl.invokeFunctor(nativeDrawGLFunctor, waitForCompletion);
|
||||
ViewRootImpl.invokeFunctor(nativeDrawGLFunctor, waitForCompletion);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,6 +43,54 @@ using namespace uirenderer;
|
||||
? (reinterpret_cast<RenderNode*>(renderNodePtr)->setPropertyFieldsDirty(dirtyFlag), true) \
|
||||
: false)
|
||||
|
||||
static JNIEnv* getenv(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;
|
||||
}
|
||||
|
||||
static jmethodID gOnRenderNodeDetached;
|
||||
|
||||
class RenderNodeContext : public VirtualLightRefBase {
|
||||
public:
|
||||
RenderNodeContext(JNIEnv* env, jobject jobjRef) {
|
||||
env->GetJavaVM(&mVm);
|
||||
// This holds a weak ref because otherwise there's a cyclic global ref
|
||||
// with this holding a strong global ref to the view which holds
|
||||
// a strong ref to RenderNode which holds a strong ref to this.
|
||||
mWeakRef = env->NewWeakGlobalRef(jobjRef);
|
||||
}
|
||||
|
||||
virtual ~RenderNodeContext() {
|
||||
JNIEnv* env = getenv(mVm);
|
||||
env->DeleteWeakGlobalRef(mWeakRef);
|
||||
}
|
||||
|
||||
jobject acquireLocalRef(JNIEnv* env) {
|
||||
return env->NewLocalRef(mWeakRef);
|
||||
}
|
||||
|
||||
private:
|
||||
JavaVM* mVm;
|
||||
jweak mWeakRef;
|
||||
};
|
||||
|
||||
// Called by ThreadedRenderer's JNI layer
|
||||
void onRenderNodeRemoved(JNIEnv* env, RenderNode* node) {
|
||||
auto context = reinterpret_cast<RenderNodeContext*>(node->getUserContext());
|
||||
if (!context) return;
|
||||
jobject jnode = context->acquireLocalRef(env);
|
||||
if (!jnode) {
|
||||
// The owning node has been GC'd, release the context
|
||||
node->setUserContext(nullptr);
|
||||
return;
|
||||
}
|
||||
env->CallVoidMethod(jnode, gOnRenderNodeDetached);
|
||||
env->DeleteLocalRef(jnode);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DisplayList view properties
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -59,7 +107,8 @@ static jint android_view_RenderNode_getDebugSize(JNIEnv* env,
|
||||
return renderNode->getDebugSize();
|
||||
}
|
||||
|
||||
static jlong android_view_RenderNode_create(JNIEnv* env, jobject clazz, jstring name) {
|
||||
static jlong android_view_RenderNode_create(JNIEnv* env, jobject thiz,
|
||||
jstring name) {
|
||||
RenderNode* renderNode = new RenderNode();
|
||||
renderNode->incStrong(0);
|
||||
if (name != NULL) {
|
||||
@@ -67,6 +116,7 @@ static jlong android_view_RenderNode_create(JNIEnv* env, jobject clazz, jstring
|
||||
renderNode->setName(textArray);
|
||||
env->ReleaseStringUTFChars(name, textArray);
|
||||
}
|
||||
renderNode->setUserContext(new RenderNodeContext(env, thiz));
|
||||
return reinterpret_cast<jlong>(renderNode);
|
||||
}
|
||||
|
||||
@@ -627,6 +677,9 @@ int register_android_view_RenderNode(JNIEnv* env) {
|
||||
jclass clazz = FindClassOrDie(env, "android/view/SurfaceView");
|
||||
gSurfaceViewPositionUpdateMethod = GetMethodIDOrDie(env, clazz,
|
||||
"updateWindowPositionRT", "(JIIII)V");
|
||||
clazz = FindClassOrDie(env, "android/view/RenderNode");
|
||||
gOnRenderNodeDetached = GetMethodIDOrDie(env, clazz,
|
||||
"onRenderNodeDetached", "()V");
|
||||
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,13 @@ private:
|
||||
std::string mMessage;
|
||||
};
|
||||
|
||||
class RootRenderNode : public RenderNode, ErrorHandler {
|
||||
// TODO: Clean this up, it's a bit odd to need to call over to
|
||||
// rendernode's jni layer. Probably means RootRenderNode should be pulled
|
||||
// into HWUI with appropriate callbacks for the various JNI hooks so
|
||||
// that RenderNode's JNI layer can handle its own thing
|
||||
void onRenderNodeRemoved(JNIEnv* env, RenderNode* node);
|
||||
|
||||
class RootRenderNode : public RenderNode, ErrorHandler, TreeObserver {
|
||||
public:
|
||||
RootRenderNode(JNIEnv* env) : RenderNode() {
|
||||
mLooper = Looper::getForThread();
|
||||
@@ -131,12 +137,15 @@ public:
|
||||
|
||||
virtual ~RootRenderNode() {}
|
||||
|
||||
virtual void onError(const std::string& message) {
|
||||
virtual void onError(const std::string& message) override {
|
||||
mLooper->sendMessage(new RenderingException(mVm, message), 0);
|
||||
}
|
||||
|
||||
virtual void prepareTree(TreeInfo& info) {
|
||||
virtual void prepareTree(TreeInfo& info) override {
|
||||
info.errorHandler = this;
|
||||
if (info.mode == TreeInfo::MODE_FULL) {
|
||||
info.observer = this;
|
||||
}
|
||||
// TODO: This is hacky
|
||||
info.windowInsetLeft = -stagingProperties().getLeft();
|
||||
info.windowInsetTop = -stagingProperties().getTop();
|
||||
@@ -145,7 +154,8 @@ public:
|
||||
info.updateWindowPositions = false;
|
||||
info.windowInsetLeft = 0;
|
||||
info.windowInsetTop = 0;
|
||||
info.errorHandler = NULL;
|
||||
info.errorHandler = nullptr;
|
||||
info.observer = nullptr;
|
||||
}
|
||||
|
||||
void sendMessage(const sp<MessageHandler>& handler) {
|
||||
@@ -171,10 +181,27 @@ public:
|
||||
mPendingAnimatingRenderNodes.clear();
|
||||
}
|
||||
|
||||
virtual void onMaybeRemovedFromTree(RenderNode* node) override {
|
||||
mMaybeRemovedNodes.insert(sp<RenderNode>(node));
|
||||
}
|
||||
|
||||
void processMaybeRemovedNodes(JNIEnv* env) {
|
||||
// We can safely access mMaybeRemovedNodes here because
|
||||
// we will only modify it in prepareTree calls that are
|
||||
// MODE_FULL
|
||||
|
||||
for (auto& node : mMaybeRemovedNodes) {
|
||||
if (node->hasParents()) continue;
|
||||
onRenderNodeRemoved(env, node.get());
|
||||
}
|
||||
mMaybeRemovedNodes.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
sp<Looper> mLooper;
|
||||
JavaVM* mVm;
|
||||
std::vector< sp<RenderNode> > mPendingAnimatingRenderNodes;
|
||||
std::set< sp<RenderNode> > mMaybeRemovedNodes;
|
||||
};
|
||||
|
||||
class AnimationContextBridge : public AnimationContext {
|
||||
@@ -472,13 +499,16 @@ static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz,
|
||||
}
|
||||
|
||||
static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
|
||||
jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) {
|
||||
jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize, jlong rootNodePtr) {
|
||||
LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE,
|
||||
"Mismatched size expectations, given %d expected %d",
|
||||
frameInfoSize, UI_THREAD_FRAME_INFO_SIZE);
|
||||
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
|
||||
RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr);
|
||||
env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo());
|
||||
return proxy->syncAndDrawFrame();
|
||||
int ret = proxy->syncAndDrawFrame();
|
||||
rootRenderNode->processMaybeRemovedNodes(env);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void android_view_ThreadedRenderer_destroy(JNIEnv* env, jobject clazz,
|
||||
@@ -705,7 +735,7 @@ static const JNINativeMethod gMethods[] = {
|
||||
{ "nSetup", "(JIIFII)V", (void*) android_view_ThreadedRenderer_setup },
|
||||
{ "nSetLightCenter", "(JFFF)V", (void*) android_view_ThreadedRenderer_setLightCenter },
|
||||
{ "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
|
||||
{ "nSyncAndDrawFrame", "(J[JI)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
|
||||
{ "nSyncAndDrawFrame", "(J[JIJ)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
|
||||
{ "nDestroy", "(JJ)V", (void*) android_view_ThreadedRenderer_destroy },
|
||||
{ "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode },
|
||||
{ "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
|
||||
|
||||
@@ -251,6 +251,7 @@ LOCAL_SRC_FILES += \
|
||||
tests/unit/LinearAllocatorTests.cpp \
|
||||
tests/unit/MatrixTests.cpp \
|
||||
tests/unit/OffscreenBufferPoolTests.cpp \
|
||||
tests/unit/RenderNodeTests.cpp \
|
||||
tests/unit/SkiaBehaviorTests.cpp \
|
||||
tests/unit/StringUtilsTests.cpp \
|
||||
tests/unit/TextDropShadowCacheTests.cpp \
|
||||
|
||||
@@ -68,7 +68,7 @@ RenderNode::RenderNode()
|
||||
}
|
||||
|
||||
RenderNode::~RenderNode() {
|
||||
deleteDisplayList();
|
||||
deleteDisplayList(nullptr);
|
||||
delete mStagingDisplayList;
|
||||
#if HWUI_NEW_OPS
|
||||
LOG_ALWAYS_FATAL_IF(mLayer, "layer missed detachment!");
|
||||
@@ -88,7 +88,7 @@ void RenderNode::setStagingDisplayList(DisplayList* displayList) {
|
||||
// If mParentCount == 0 we are the sole reference to this RenderNode,
|
||||
// so immediately free the old display list
|
||||
if (!mParentCount && !mStagingDisplayList) {
|
||||
deleteDisplayList();
|
||||
deleteDisplayList(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@ void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void RenderNode::syncDisplayList() {
|
||||
void RenderNode::syncDisplayList(TreeObserver* observer) {
|
||||
// Make sure we inc first so that we don't fluctuate between 0 and 1,
|
||||
// which would thrash the layer cache
|
||||
if (mStagingDisplayList) {
|
||||
@@ -470,7 +470,7 @@ void RenderNode::syncDisplayList() {
|
||||
child->renderNode->incParentRefCount();
|
||||
}
|
||||
}
|
||||
deleteDisplayList();
|
||||
deleteDisplayList(observer);
|
||||
mDisplayList = mStagingDisplayList;
|
||||
mStagingDisplayList = nullptr;
|
||||
if (mDisplayList) {
|
||||
@@ -486,15 +486,15 @@ void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) {
|
||||
// Damage with the old display list first then the new one to catch any
|
||||
// changes in isRenderable or, in the future, bounds
|
||||
damageSelf(info);
|
||||
syncDisplayList();
|
||||
syncDisplayList(info.observer);
|
||||
damageSelf(info);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderNode::deleteDisplayList() {
|
||||
void RenderNode::deleteDisplayList(TreeObserver* observer) {
|
||||
if (mDisplayList) {
|
||||
for (auto&& child : mDisplayList->getChildren()) {
|
||||
child->renderNode->decParentRefCount();
|
||||
child->renderNode->decParentRefCount(observer);
|
||||
}
|
||||
}
|
||||
delete mDisplayList;
|
||||
@@ -526,32 +526,35 @@ void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayL
|
||||
}
|
||||
}
|
||||
|
||||
void RenderNode::destroyHardwareResources() {
|
||||
void RenderNode::destroyHardwareResources(TreeObserver* observer) {
|
||||
if (mLayer) {
|
||||
destroyLayer(mLayer);
|
||||
mLayer = nullptr;
|
||||
}
|
||||
if (mDisplayList) {
|
||||
for (auto&& child : mDisplayList->getChildren()) {
|
||||
child->renderNode->destroyHardwareResources();
|
||||
child->renderNode->destroyHardwareResources(observer);
|
||||
}
|
||||
if (mNeedsDisplayListSync) {
|
||||
// Next prepare tree we are going to push a new display list, so we can
|
||||
// drop our current one now
|
||||
deleteDisplayList();
|
||||
deleteDisplayList(observer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenderNode::decParentRefCount() {
|
||||
void RenderNode::decParentRefCount(TreeObserver* observer) {
|
||||
LOG_ALWAYS_FATAL_IF(!mParentCount, "already 0!");
|
||||
mParentCount--;
|
||||
if (!mParentCount) {
|
||||
if (observer) {
|
||||
observer->onMaybeRemovedFromTree(this);
|
||||
}
|
||||
// If a child of ours is being attached to our parent then this will incorrectly
|
||||
// destroy its hardware resources. However, this situation is highly unlikely
|
||||
// and the failure is "just" that the layer is re-created, so this should
|
||||
// be safe enough
|
||||
destroyHardwareResources();
|
||||
destroyHardwareResources(observer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ class SaveLayerOp;
|
||||
class SaveOp;
|
||||
class RestoreToCountOp;
|
||||
class TreeInfo;
|
||||
class TreeObserver;
|
||||
|
||||
namespace proto {
|
||||
class RenderNode;
|
||||
@@ -154,6 +155,14 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
VirtualLightRefBase* getUserContext() const {
|
||||
return mUserContext.get();
|
||||
}
|
||||
|
||||
void setUserContext(VirtualLightRefBase* context) {
|
||||
mUserContext = context;
|
||||
}
|
||||
|
||||
bool isPropertyFieldDirty(DirtyPropertyMask field) const {
|
||||
return mDirtyPropertyFields & field;
|
||||
}
|
||||
@@ -187,7 +196,7 @@ public:
|
||||
}
|
||||
|
||||
ANDROID_API virtual void prepareTree(TreeInfo& info);
|
||||
void destroyHardwareResources();
|
||||
void destroyHardwareResources(TreeObserver* observer);
|
||||
|
||||
// UI thread only!
|
||||
ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator);
|
||||
@@ -232,6 +241,12 @@ public:
|
||||
mPositionListener.reset(listener);
|
||||
}
|
||||
|
||||
// This is only modified in MODE_FULL, so it can be safely accessed
|
||||
// on the UI thread.
|
||||
ANDROID_API bool hasParents() {
|
||||
return mParentCount;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair;
|
||||
|
||||
@@ -291,7 +306,7 @@ private:
|
||||
|
||||
|
||||
void syncProperties();
|
||||
void syncDisplayList();
|
||||
void syncDisplayList(TreeObserver* observer);
|
||||
|
||||
void prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer);
|
||||
void pushStagingPropertiesChanges(TreeInfo& info);
|
||||
@@ -302,13 +317,14 @@ private:
|
||||
#endif
|
||||
void prepareLayer(TreeInfo& info, uint32_t dirtyMask);
|
||||
void pushLayerUpdate(TreeInfo& info);
|
||||
void deleteDisplayList();
|
||||
void deleteDisplayList(TreeObserver* observer);
|
||||
void damageSelf(TreeInfo& info);
|
||||
|
||||
void incParentRefCount() { mParentCount++; }
|
||||
void decParentRefCount();
|
||||
void decParentRefCount(TreeObserver* observer);
|
||||
|
||||
String8 mName;
|
||||
sp<VirtualLightRefBase> mUserContext;
|
||||
|
||||
uint32_t mDirtyPropertyFields;
|
||||
RenderProperties mProperties;
|
||||
|
||||
@@ -32,6 +32,7 @@ class CanvasContext;
|
||||
class DamageAccumulator;
|
||||
class LayerUpdateQueue;
|
||||
class OpenGLRenderer;
|
||||
class RenderNode;
|
||||
class RenderState;
|
||||
|
||||
class ErrorHandler {
|
||||
@@ -41,6 +42,17 @@ protected:
|
||||
~ErrorHandler() {}
|
||||
};
|
||||
|
||||
class TreeObserver {
|
||||
public:
|
||||
// Called when a RenderNode's parent count hits 0.
|
||||
// Due to the unordered nature of tree pushes, once prepareTree
|
||||
// is finished it is possible that the node was "resurrected" and has
|
||||
// a non-zero parent count.
|
||||
virtual void onMaybeRemovedFromTree(RenderNode* node) {}
|
||||
protected:
|
||||
~TreeObserver() {}
|
||||
};
|
||||
|
||||
// This would be a struct, but we want to PREVENT_COPY_AND_ASSIGN
|
||||
class TreeInfo {
|
||||
PREVENT_COPY_AND_ASSIGN(TreeInfo);
|
||||
@@ -86,6 +98,10 @@ public:
|
||||
#endif
|
||||
ErrorHandler* errorHandler = nullptr;
|
||||
|
||||
// Optional, may be nullptr. Used to allow things to observe interesting
|
||||
// tree state changes
|
||||
TreeObserver* observer = nullptr;
|
||||
|
||||
// Frame number for use with synchronized surfaceview position updating
|
||||
int64_t frameNumber = -1;
|
||||
int32_t windowInsetLeft = 0;
|
||||
|
||||
@@ -576,7 +576,7 @@ void CanvasContext::markLayerInUse(RenderNode* node) {
|
||||
|
||||
static void destroyPrefetechedNode(RenderNode* node) {
|
||||
ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", node->getName());
|
||||
node->destroyHardwareResources();
|
||||
node->destroyHardwareResources(nullptr);
|
||||
node->decStrong(nullptr);
|
||||
}
|
||||
|
||||
@@ -641,7 +641,7 @@ void CanvasContext::destroyHardwareResources() {
|
||||
if (mEglManager.hasEglContext()) {
|
||||
freePrefetechedLayers();
|
||||
for (const sp<RenderNode>& node : mRenderNodes) {
|
||||
node->destroyHardwareResources();
|
||||
node->destroyHardwareResources(nullptr);
|
||||
}
|
||||
Caches& caches = Caches::getInstance();
|
||||
// Make sure to release all the textures we were owning as there won't
|
||||
|
||||
@@ -218,7 +218,7 @@ public:
|
||||
private:
|
||||
static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
|
||||
node->syncProperties();
|
||||
node->syncDisplayList();
|
||||
node->syncDisplayList(nullptr);
|
||||
auto displayList = node->getDisplayList();
|
||||
if (displayList) {
|
||||
for (auto&& childOp : displayList->getChildren()) {
|
||||
|
||||
53
libs/hwui/tests/unit/RenderNodeTests.cpp
Normal file
53
libs/hwui/tests/unit/RenderNodeTests.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "RenderNode.h"
|
||||
#include "TreeInfo.h"
|
||||
#include "tests/common/TestUtils.h"
|
||||
#include "utils/Color.h"
|
||||
|
||||
using namespace android;
|
||||
using namespace android::uirenderer;
|
||||
|
||||
TEST(RenderNode, hasParents) {
|
||||
auto child = TestUtils::createNode(0, 0, 200, 400,
|
||||
[](RenderProperties& props, TestCanvas& canvas) {
|
||||
canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode);
|
||||
});
|
||||
auto parent = TestUtils::createNode(0, 0, 200, 400,
|
||||
[&child](RenderProperties& props, TestCanvas& canvas) {
|
||||
canvas.drawRenderNode(child.get());
|
||||
});
|
||||
|
||||
TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
|
||||
|
||||
EXPECT_TRUE(child->hasParents()) << "Child node has no parent";
|
||||
EXPECT_FALSE(parent->hasParents()) << "Root node shouldn't have any parents";
|
||||
|
||||
TestUtils::recordNode(*parent, [](TestCanvas& canvas) {
|
||||
canvas.drawColor(Color::Amber_500, SkXfermode::kSrcOver_Mode);
|
||||
});
|
||||
|
||||
EXPECT_TRUE(child->hasParents()) << "Child should still have a parent";
|
||||
EXPECT_FALSE(parent->hasParents()) << "Root node shouldn't have any parents";
|
||||
|
||||
TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
|
||||
|
||||
EXPECT_FALSE(child->hasParents()) << "Child should be removed";
|
||||
EXPECT_FALSE(parent->hasParents()) << "Root node shouldn't have any parents";
|
||||
}
|
||||
Reference in New Issue
Block a user