Loading core/java/android/view/ThreadedRenderer.java +61 −0 Original line number Diff line number Diff line Loading @@ -108,6 +108,11 @@ public class ThreadedRenderer extends HardwareRenderer { private Choreographer mChoreographer; private boolean mRootNodeNeedsUpdate; // In case of multi threaded render nodes, these bounds indicate the content bounds against // which the backdrop needs to be cropped against. private final Rect mCurrentContentBounds = new Rect(); private final Rect mStagedContentBounds = new Rect(); ThreadedRenderer(Context context, boolean translucent) { final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0); mLightY = a.getDimension(R.styleable.Lighting_lightY, 0); Loading Loading @@ -307,6 +312,47 @@ public class ThreadedRenderer extends HardwareRenderer { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } /** * Adds a rendernode to the renderer which can be drawn and changed asynchronously to the * rendernode of the UI thread. * @param node The node to add. * @param placeFront If true, the render node will be placed in front of the content node, * otherwise behind the content node. */ public void addRenderNode(RenderNode node, boolean placeFront) { nAddRenderNode(mNativeProxy, node.mNativeRenderNode, placeFront); } /** * Only especially added render nodes can be removed. * @param node The node which was added via addRenderNode which should get removed again. */ public void removeRenderNode(RenderNode node) { nRemoveRenderNode(mNativeProxy, node.mNativeRenderNode); } /** * Draws a particular render node. If the node is not the content node, only the additional * nodes will get drawn and the content remains untouched. * @param node The node to be drawn. */ public void drawRenderNode(RenderNode node) { nDrawRenderNode(mNativeProxy, node.mNativeRenderNode); } /** * To avoid unnecessary overdrawing of the main content all additionally passed render nodes * will be prevented to overdraw this area. It will be synchronized with the draw call. * This should be updated in the content view's draw call. * @param left The left side of the protected bounds. * @param top The top side of the protected bounds. * @param right The right side of the protected bounds. * @param bottom The bottom side of the protected bounds. */ public void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) { mStagedContentBounds.set(left, top, right, bottom); } @Override void invalidateRoot() { mRootNodeNeedsUpdate = true; Loading @@ -320,6 +366,14 @@ public class ThreadedRenderer extends HardwareRenderer { choreographer.mFrameInfo.markDrawStart(); updateRootDisplayList(view, callbacks); // The main content view was updating the content bounds and we transfer them to the // renderer. if (!mCurrentContentBounds.equals(mStagedContentBounds)) { mCurrentContentBounds.set(mStagedContentBounds); nSetContentOverdrawProtectionBounds(mNativeProxy, mCurrentContentBounds.left, mCurrentContentBounds.top, mCurrentContentBounds.right, mCurrentContentBounds.bottom); } attachInfo.mIgnoreDirtyState = false; Loading Loading @@ -541,4 +595,11 @@ public class ThreadedRenderer extends HardwareRenderer { private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd, @DumpFlags int dumpFlags); private static native void nDumpProfileData(byte[] data, FileDescriptor fd); private static native void nAddRenderNode(long nativeProxy, long rootRenderNode, boolean placeFront); private static native void nRemoveRenderNode(long nativeProxy, long rootRenderNode); private static native void nDrawRenderNode(long nativeProxy, long rootRenderNode); private static native void nSetContentOverdrawProtectionBounds(long nativeProxy, int left, int top, int right, int bottom); } core/java/android/view/View.java +17 −0 Original line number Diff line number Diff line Loading @@ -7372,6 +7372,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } /** * Compute the view's coordinate within the surface. * * <p>Computes the coordinates of this view in its surface. The argument * must be an array of two integers. After the method returns, the array * contains the x and y location in that order.</p> * @hide * @param location an array of two integers in which to hold the coordinates */ public void getLocationInSurface(@Size(2) int[] location) { getLocationInWindow(location); if (mAttachInfo != null && mAttachInfo.mViewRootImpl != null) { location[0] += mAttachInfo.mViewRootImpl.mWindowAttributes.surfaceInsets.left; location[1] += mAttachInfo.mViewRootImpl.mWindowAttributes.surfaceInsets.top; } } /** * Provide original WindowInsets that are dispatched to the view hierarchy. The insets are * only available if the view is attached. Loading core/jni/android_view_ThreadedRenderer.cpp +31 −1 Original line number Diff line number Diff line Loading @@ -440,6 +440,32 @@ static void android_view_ThreadedRenderer_dumpProfileData(JNIEnv* env, jobject c } } static void android_view_ThreadedRenderer_addRenderNode(JNIEnv* env, jobject clazz, jlong proxyPtr, jlong renderNodePtr, jboolean placeFront) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); proxy->addRenderNode(renderNode, placeFront); } static void android_view_ThreadedRenderer_removeRenderNode(JNIEnv* env, jobject clazz, jlong proxyPtr, jlong renderNodePtr) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); proxy->removeRenderNode(renderNode); } static void android_view_ThreadedRendererd_drawRenderNode(JNIEnv* env, jobject clazz, jlong proxyPtr, jlong renderNodePtr) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); proxy->drawRenderNode(renderNode); } static void android_view_ThreadedRenderer_setContentOverdrawProtectionBounds(JNIEnv* env, jobject clazz, jlong proxyPtr, jint left, jint top, jint right, jint bottom) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); proxy->setContentOverdrawProtectionBounds(left, top, right, bottom); } // ---------------------------------------------------------------------------- // Shaders Loading @@ -447,7 +473,6 @@ static void android_view_ThreadedRenderer_dumpProfileData(JNIEnv* env, jobject c static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz, jstring diskCachePath) { const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL); egl_cache_t::get()->setCacheFilename(cacheArray); env->ReleaseStringUTFChars(diskCachePath, cacheArray); Loading Loading @@ -494,6 +519,11 @@ static JNINativeMethod gMethods[] = { { "nDumpProfileData", "([BLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileData }, { "setupShadersDiskCache", "(Ljava/lang/String;)V", (void*) android_view_ThreadedRenderer_setupShadersDiskCache }, { "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode}, { "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode}, { "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode}, { "nSetContentOverdrawProtectionBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentOverdrawProtectionBounds}, }; int register_android_view_ThreadedRenderer(JNIEnv* env) { Loading libs/hwui/TreeInfo.h +1 −1 Original line number Diff line number Diff line Loading @@ -77,7 +77,7 @@ public: , canvasContext(clone.canvasContext) {} const TraversalMode mode; TraversalMode mode; // TODO: Remove this? Currently this is used to signal to stop preparing // textures if we run out of cache space. bool prepareTextures; Loading libs/hwui/renderthread/CanvasContext.cpp +108 −7 Original line number Diff line number Diff line Loading @@ -60,9 +60,10 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, , mEglManager(thread.eglManager()) , mOpaque(!translucent) , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord())) , mRootRenderNode(rootRenderNode) , mJankTracker(thread.timeLord().frameIntervalNanos()) , mProfiler(mFrames) { , mProfiler(mFrames) , mContentOverdrawProtectionBounds(0, 0, 0, 0) { mRenderNodes.emplace_back(rootRenderNode); mRenderThread.renderState().registerCanvasContext(this); mProfiler.setDensity(mRenderThread.mainDisplayInfo().density); } Loading Loading @@ -172,7 +173,8 @@ static bool wasSkipped(FrameInfo* info) { return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame); } void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued) { void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target) { mRenderThread.removeFrameCallback(this); // If the previous frame was dropped we don't need to hold onto it, so Loading @@ -189,7 +191,13 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy info.canvasContext = this; mAnimationContext->startFrame(info.mode); mRootRenderNode->prepareTree(info); for (const sp<RenderNode>& node : mRenderNodes) { // Only the primary target node will be drawn full - all other nodes would get drawn in // real time mode. In case of a window, the primary node is the window content and the other // node(s) are non client / filler nodes. info.mode = (node.get() == target ? TreeInfo::MODE_FULL : TreeInfo::MODE_RT_ONLY); node->prepareTree(info); } mAnimationContext->runRemainingAnimations(info); freePrefetechedLayers(); Loading Loading @@ -299,7 +307,95 @@ void CanvasContext::draw() { dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque); Rect outBounds; mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); // It there are multiple render nodes, they are as follows: // #0 - backdrop // #1 - content (with - and clipped to - bounds mContentOverdrawProtectionBounds) // #2 - frame // Usually the backdrop cannot be seen since it will be entirely covered by the content. While // resizing however it might become partially visible. The following render loop will crop the // backdrop against the content and draw the remaining part of it. It will then crop the content // against the backdrop (since that indicates a shrinking of the window) and then the frame // around everything. // The bounds of the backdrop against which the content should be clipped. Rect backdropBounds = mContentOverdrawProtectionBounds; // If there is no content bounds we ignore the layering as stated above and start with 2. int layer = mContentOverdrawProtectionBounds.isEmpty() ? 2 : 0; // Draw all render nodes. Note that for (const sp<RenderNode>& node : mRenderNodes) { if (layer == 0) { // Backdrop. // Draw the backdrop clipped to the inverse content bounds. const RenderProperties& properties = node->properties(); Rect targetBounds(properties.getLeft(), properties.getTop(), properties.getRight(), properties.getBottom()); // Remember the intersection of the target bounds and the intersection bounds against // which we have to crop the content. backdropBounds.intersect(targetBounds); // Check if we have to draw something on the left side ... if (targetBounds.left < mContentOverdrawProtectionBounds.left) { mCanvas->save(SkCanvas::kClip_SaveFlag); if (mCanvas->clipRect(targetBounds.left, targetBounds.top, mContentOverdrawProtectionBounds.left, targetBounds.bottom, SkRegion::kIntersect_Op)) { mCanvas->drawRenderNode(node.get(), outBounds); } // Reduce the target area by the area we have just painted. targetBounds.left = std::min(mContentOverdrawProtectionBounds.left, targetBounds.right); mCanvas->restore(); } // ... or on the right side ... if (targetBounds.right > mContentOverdrawProtectionBounds.right && !targetBounds.isEmpty()) { mCanvas->save(SkCanvas::kClip_SaveFlag); if (mCanvas->clipRect(mContentOverdrawProtectionBounds.right, targetBounds.top, targetBounds.right, targetBounds.bottom, SkRegion::kIntersect_Op)) { mCanvas->drawRenderNode(node.get(), outBounds); } // Reduce the target area by the area we have just painted. targetBounds.right = std::max(targetBounds.left, mContentOverdrawProtectionBounds.right); mCanvas->restore(); } // ... or at the top ... if (targetBounds.top < mContentOverdrawProtectionBounds.top && !targetBounds.isEmpty()) { mCanvas->save(SkCanvas::kClip_SaveFlag); if (mCanvas->clipRect(targetBounds.left, targetBounds.top, targetBounds.right, mContentOverdrawProtectionBounds.top, SkRegion::kIntersect_Op)) { mCanvas->drawRenderNode(node.get(), outBounds); } // Reduce the target area by the area we have just painted. targetBounds.top = std::min(mContentOverdrawProtectionBounds.top, targetBounds.bottom); mCanvas->restore(); } // ... or at the bottom. if (targetBounds.bottom > mContentOverdrawProtectionBounds.bottom && !targetBounds.isEmpty()) { mCanvas->save(SkCanvas::kClip_SaveFlag); if (mCanvas->clipRect(targetBounds.left, mContentOverdrawProtectionBounds.bottom, targetBounds.right, targetBounds.bottom, SkRegion::kIntersect_Op)) { mCanvas->drawRenderNode(node.get(), outBounds); } mCanvas->restore(); } } else if (layer == 1) { // Content // It gets cropped against the bounds of the backdrop to stay inside. mCanvas->save(SkCanvas::kClip_SaveFlag); if (mCanvas->clipRect(backdropBounds.left, backdropBounds.top, backdropBounds.right, backdropBounds.bottom, SkRegion::kIntersect_Op)) { mCanvas->drawRenderNode(node.get(), outBounds); } mCanvas->restore(); } else { // draw the rest on top at will! mCanvas->drawRenderNode(node.get(), outBounds); } layer++; } profiler().draw(mCanvas); Loading Loading @@ -343,7 +439,10 @@ void CanvasContext::doFrame() { if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) { return; } prepareAndDraw(nullptr); } void CanvasContext::prepareAndDraw(RenderNode* node) { ATRACE_CALL(); int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE]; Loading @@ -353,7 +452,7 @@ void CanvasContext::doFrame() { mRenderThread.timeLord().latestVsync()); TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState()); prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC)); prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC), node); if (info.out.canDrawThisFrame) { draw(); } Loading Loading @@ -423,7 +522,9 @@ void CanvasContext::destroyHardwareResources() { stopDrawing(); if (mEglManager.hasEglContext()) { freePrefetechedLayers(); mRootRenderNode->destroyHardwareResources(); for (const sp<RenderNode>& node : mRenderNodes) { node->destroyHardwareResources(); } Caches& caches = Caches::getInstance(); // Make sure to release all the textures we were owning as there won't // be another draw Loading Loading
core/java/android/view/ThreadedRenderer.java +61 −0 Original line number Diff line number Diff line Loading @@ -108,6 +108,11 @@ public class ThreadedRenderer extends HardwareRenderer { private Choreographer mChoreographer; private boolean mRootNodeNeedsUpdate; // In case of multi threaded render nodes, these bounds indicate the content bounds against // which the backdrop needs to be cropped against. private final Rect mCurrentContentBounds = new Rect(); private final Rect mStagedContentBounds = new Rect(); ThreadedRenderer(Context context, boolean translucent) { final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0); mLightY = a.getDimension(R.styleable.Lighting_lightY, 0); Loading Loading @@ -307,6 +312,47 @@ public class ThreadedRenderer extends HardwareRenderer { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } /** * Adds a rendernode to the renderer which can be drawn and changed asynchronously to the * rendernode of the UI thread. * @param node The node to add. * @param placeFront If true, the render node will be placed in front of the content node, * otherwise behind the content node. */ public void addRenderNode(RenderNode node, boolean placeFront) { nAddRenderNode(mNativeProxy, node.mNativeRenderNode, placeFront); } /** * Only especially added render nodes can be removed. * @param node The node which was added via addRenderNode which should get removed again. */ public void removeRenderNode(RenderNode node) { nRemoveRenderNode(mNativeProxy, node.mNativeRenderNode); } /** * Draws a particular render node. If the node is not the content node, only the additional * nodes will get drawn and the content remains untouched. * @param node The node to be drawn. */ public void drawRenderNode(RenderNode node) { nDrawRenderNode(mNativeProxy, node.mNativeRenderNode); } /** * To avoid unnecessary overdrawing of the main content all additionally passed render nodes * will be prevented to overdraw this area. It will be synchronized with the draw call. * This should be updated in the content view's draw call. * @param left The left side of the protected bounds. * @param top The top side of the protected bounds. * @param right The right side of the protected bounds. * @param bottom The bottom side of the protected bounds. */ public void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) { mStagedContentBounds.set(left, top, right, bottom); } @Override void invalidateRoot() { mRootNodeNeedsUpdate = true; Loading @@ -320,6 +366,14 @@ public class ThreadedRenderer extends HardwareRenderer { choreographer.mFrameInfo.markDrawStart(); updateRootDisplayList(view, callbacks); // The main content view was updating the content bounds and we transfer them to the // renderer. if (!mCurrentContentBounds.equals(mStagedContentBounds)) { mCurrentContentBounds.set(mStagedContentBounds); nSetContentOverdrawProtectionBounds(mNativeProxy, mCurrentContentBounds.left, mCurrentContentBounds.top, mCurrentContentBounds.right, mCurrentContentBounds.bottom); } attachInfo.mIgnoreDirtyState = false; Loading Loading @@ -541,4 +595,11 @@ public class ThreadedRenderer extends HardwareRenderer { private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd, @DumpFlags int dumpFlags); private static native void nDumpProfileData(byte[] data, FileDescriptor fd); private static native void nAddRenderNode(long nativeProxy, long rootRenderNode, boolean placeFront); private static native void nRemoveRenderNode(long nativeProxy, long rootRenderNode); private static native void nDrawRenderNode(long nativeProxy, long rootRenderNode); private static native void nSetContentOverdrawProtectionBounds(long nativeProxy, int left, int top, int right, int bottom); }
core/java/android/view/View.java +17 −0 Original line number Diff line number Diff line Loading @@ -7372,6 +7372,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } /** * Compute the view's coordinate within the surface. * * <p>Computes the coordinates of this view in its surface. The argument * must be an array of two integers. After the method returns, the array * contains the x and y location in that order.</p> * @hide * @param location an array of two integers in which to hold the coordinates */ public void getLocationInSurface(@Size(2) int[] location) { getLocationInWindow(location); if (mAttachInfo != null && mAttachInfo.mViewRootImpl != null) { location[0] += mAttachInfo.mViewRootImpl.mWindowAttributes.surfaceInsets.left; location[1] += mAttachInfo.mViewRootImpl.mWindowAttributes.surfaceInsets.top; } } /** * Provide original WindowInsets that are dispatched to the view hierarchy. The insets are * only available if the view is attached. Loading
core/jni/android_view_ThreadedRenderer.cpp +31 −1 Original line number Diff line number Diff line Loading @@ -440,6 +440,32 @@ static void android_view_ThreadedRenderer_dumpProfileData(JNIEnv* env, jobject c } } static void android_view_ThreadedRenderer_addRenderNode(JNIEnv* env, jobject clazz, jlong proxyPtr, jlong renderNodePtr, jboolean placeFront) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); proxy->addRenderNode(renderNode, placeFront); } static void android_view_ThreadedRenderer_removeRenderNode(JNIEnv* env, jobject clazz, jlong proxyPtr, jlong renderNodePtr) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); proxy->removeRenderNode(renderNode); } static void android_view_ThreadedRendererd_drawRenderNode(JNIEnv* env, jobject clazz, jlong proxyPtr, jlong renderNodePtr) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); proxy->drawRenderNode(renderNode); } static void android_view_ThreadedRenderer_setContentOverdrawProtectionBounds(JNIEnv* env, jobject clazz, jlong proxyPtr, jint left, jint top, jint right, jint bottom) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); proxy->setContentOverdrawProtectionBounds(left, top, right, bottom); } // ---------------------------------------------------------------------------- // Shaders Loading @@ -447,7 +473,6 @@ static void android_view_ThreadedRenderer_dumpProfileData(JNIEnv* env, jobject c static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz, jstring diskCachePath) { const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL); egl_cache_t::get()->setCacheFilename(cacheArray); env->ReleaseStringUTFChars(diskCachePath, cacheArray); Loading Loading @@ -494,6 +519,11 @@ static JNINativeMethod gMethods[] = { { "nDumpProfileData", "([BLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileData }, { "setupShadersDiskCache", "(Ljava/lang/String;)V", (void*) android_view_ThreadedRenderer_setupShadersDiskCache }, { "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode}, { "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode}, { "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode}, { "nSetContentOverdrawProtectionBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentOverdrawProtectionBounds}, }; int register_android_view_ThreadedRenderer(JNIEnv* env) { Loading
libs/hwui/TreeInfo.h +1 −1 Original line number Diff line number Diff line Loading @@ -77,7 +77,7 @@ public: , canvasContext(clone.canvasContext) {} const TraversalMode mode; TraversalMode mode; // TODO: Remove this? Currently this is used to signal to stop preparing // textures if we run out of cache space. bool prepareTextures; Loading
libs/hwui/renderthread/CanvasContext.cpp +108 −7 Original line number Diff line number Diff line Loading @@ -60,9 +60,10 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, , mEglManager(thread.eglManager()) , mOpaque(!translucent) , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord())) , mRootRenderNode(rootRenderNode) , mJankTracker(thread.timeLord().frameIntervalNanos()) , mProfiler(mFrames) { , mProfiler(mFrames) , mContentOverdrawProtectionBounds(0, 0, 0, 0) { mRenderNodes.emplace_back(rootRenderNode); mRenderThread.renderState().registerCanvasContext(this); mProfiler.setDensity(mRenderThread.mainDisplayInfo().density); } Loading Loading @@ -172,7 +173,8 @@ static bool wasSkipped(FrameInfo* info) { return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame); } void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued) { void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target) { mRenderThread.removeFrameCallback(this); // If the previous frame was dropped we don't need to hold onto it, so Loading @@ -189,7 +191,13 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy info.canvasContext = this; mAnimationContext->startFrame(info.mode); mRootRenderNode->prepareTree(info); for (const sp<RenderNode>& node : mRenderNodes) { // Only the primary target node will be drawn full - all other nodes would get drawn in // real time mode. In case of a window, the primary node is the window content and the other // node(s) are non client / filler nodes. info.mode = (node.get() == target ? TreeInfo::MODE_FULL : TreeInfo::MODE_RT_ONLY); node->prepareTree(info); } mAnimationContext->runRemainingAnimations(info); freePrefetechedLayers(); Loading Loading @@ -299,7 +307,95 @@ void CanvasContext::draw() { dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque); Rect outBounds; mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); // It there are multiple render nodes, they are as follows: // #0 - backdrop // #1 - content (with - and clipped to - bounds mContentOverdrawProtectionBounds) // #2 - frame // Usually the backdrop cannot be seen since it will be entirely covered by the content. While // resizing however it might become partially visible. The following render loop will crop the // backdrop against the content and draw the remaining part of it. It will then crop the content // against the backdrop (since that indicates a shrinking of the window) and then the frame // around everything. // The bounds of the backdrop against which the content should be clipped. Rect backdropBounds = mContentOverdrawProtectionBounds; // If there is no content bounds we ignore the layering as stated above and start with 2. int layer = mContentOverdrawProtectionBounds.isEmpty() ? 2 : 0; // Draw all render nodes. Note that for (const sp<RenderNode>& node : mRenderNodes) { if (layer == 0) { // Backdrop. // Draw the backdrop clipped to the inverse content bounds. const RenderProperties& properties = node->properties(); Rect targetBounds(properties.getLeft(), properties.getTop(), properties.getRight(), properties.getBottom()); // Remember the intersection of the target bounds and the intersection bounds against // which we have to crop the content. backdropBounds.intersect(targetBounds); // Check if we have to draw something on the left side ... if (targetBounds.left < mContentOverdrawProtectionBounds.left) { mCanvas->save(SkCanvas::kClip_SaveFlag); if (mCanvas->clipRect(targetBounds.left, targetBounds.top, mContentOverdrawProtectionBounds.left, targetBounds.bottom, SkRegion::kIntersect_Op)) { mCanvas->drawRenderNode(node.get(), outBounds); } // Reduce the target area by the area we have just painted. targetBounds.left = std::min(mContentOverdrawProtectionBounds.left, targetBounds.right); mCanvas->restore(); } // ... or on the right side ... if (targetBounds.right > mContentOverdrawProtectionBounds.right && !targetBounds.isEmpty()) { mCanvas->save(SkCanvas::kClip_SaveFlag); if (mCanvas->clipRect(mContentOverdrawProtectionBounds.right, targetBounds.top, targetBounds.right, targetBounds.bottom, SkRegion::kIntersect_Op)) { mCanvas->drawRenderNode(node.get(), outBounds); } // Reduce the target area by the area we have just painted. targetBounds.right = std::max(targetBounds.left, mContentOverdrawProtectionBounds.right); mCanvas->restore(); } // ... or at the top ... if (targetBounds.top < mContentOverdrawProtectionBounds.top && !targetBounds.isEmpty()) { mCanvas->save(SkCanvas::kClip_SaveFlag); if (mCanvas->clipRect(targetBounds.left, targetBounds.top, targetBounds.right, mContentOverdrawProtectionBounds.top, SkRegion::kIntersect_Op)) { mCanvas->drawRenderNode(node.get(), outBounds); } // Reduce the target area by the area we have just painted. targetBounds.top = std::min(mContentOverdrawProtectionBounds.top, targetBounds.bottom); mCanvas->restore(); } // ... or at the bottom. if (targetBounds.bottom > mContentOverdrawProtectionBounds.bottom && !targetBounds.isEmpty()) { mCanvas->save(SkCanvas::kClip_SaveFlag); if (mCanvas->clipRect(targetBounds.left, mContentOverdrawProtectionBounds.bottom, targetBounds.right, targetBounds.bottom, SkRegion::kIntersect_Op)) { mCanvas->drawRenderNode(node.get(), outBounds); } mCanvas->restore(); } } else if (layer == 1) { // Content // It gets cropped against the bounds of the backdrop to stay inside. mCanvas->save(SkCanvas::kClip_SaveFlag); if (mCanvas->clipRect(backdropBounds.left, backdropBounds.top, backdropBounds.right, backdropBounds.bottom, SkRegion::kIntersect_Op)) { mCanvas->drawRenderNode(node.get(), outBounds); } mCanvas->restore(); } else { // draw the rest on top at will! mCanvas->drawRenderNode(node.get(), outBounds); } layer++; } profiler().draw(mCanvas); Loading Loading @@ -343,7 +439,10 @@ void CanvasContext::doFrame() { if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) { return; } prepareAndDraw(nullptr); } void CanvasContext::prepareAndDraw(RenderNode* node) { ATRACE_CALL(); int64_t frameInfo[UI_THREAD_FRAME_INFO_SIZE]; Loading @@ -353,7 +452,7 @@ void CanvasContext::doFrame() { mRenderThread.timeLord().latestVsync()); TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState()); prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC)); prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC), node); if (info.out.canDrawThisFrame) { draw(); } Loading Loading @@ -423,7 +522,9 @@ void CanvasContext::destroyHardwareResources() { stopDrawing(); if (mEglManager.hasEglContext()) { freePrefetechedLayers(); mRootRenderNode->destroyHardwareResources(); for (const sp<RenderNode>& node : mRenderNodes) { node->destroyHardwareResources(); } Caches& caches = Caches::getInstance(); // Make sure to release all the textures we were owning as there won't // be another draw Loading