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

Commit fe1f99c2 authored by Stefan Kuhne's avatar Stefan Kuhne Committed by Android (Google) Code Review
Browse files

Merge "MultiThreaded rendering of different renderNodes"

parents fd83681a ea7a7fb7
Loading
Loading
Loading
Loading
+61 −0
Original line number Diff line number Diff line
@@ -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);
@@ -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;
@@ -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;

@@ -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);
}
+17 −0
Original line number Diff line number Diff line
@@ -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.
+31 −1
Original line number Diff line number Diff line
@@ -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
@@ -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);
@@ -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) {
+1 −1
Original line number Diff line number Diff line
@@ -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;
+108 −7
Original line number Diff line number Diff line
@@ -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);
}
@@ -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
@@ -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();
@@ -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);

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