MultiThreaded rendering of different renderNodes
This is adding the renderer side infrastructure to allow
rendering multiple render nodes with different threads.
This is a pre-step for decoupling a non client decor
resize reder from a content resize render.
Multiple render nodes can be added to be drawn, and to
prevent overdrawing, a content bounds area can be set
Bug: 22527834
Change-Id: Ie7271e20895bf38957e5a84aeefc883e282039ad
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b74b508..9dc5b45 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -60,9 +60,10 @@
, 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 6a79320..1c3845c 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -34,6 +34,7 @@
#include <set>
#include <string>
+#include <vector>
namespace android {
namespace uirenderer {
@@ -77,12 +78,14 @@
void setOpaque(bool opaque);
void makeCurrent();
void processLayerUpdate(DeferredLayerUpdater* layerUpdater);
- void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued);
+ void prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
+ int64_t syncQueued, RenderNode* target);
void draw();
void destroy();
// IFrameCallback, Chroreographer-driven frame callback entry point
virtual void doFrame() override;
+ void prepareAndDraw(RenderNode* node);
void buildLayer(RenderNode* node);
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
@@ -113,6 +116,20 @@
void serializeDisplayListTree();
+ void addRenderNode(RenderNode* node, bool placeFront) {
+ int pos = placeFront ? 0 : static_cast<int>(mRenderNodes.size());
+ mRenderNodes.emplace( mRenderNodes.begin() + pos, node);
+ }
+
+ void removeRenderNode(RenderNode* node) {
+ mRenderNodes.erase(std::remove(mRenderNodes.begin(), mRenderNodes.end(), node),
+ mRenderNodes.end());
+ }
+
+ void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
+ mContentOverdrawProtectionBounds.set(left, top, right, bottom);
+ }
+
private:
friend class RegisterFrameCallbackTask;
// TODO: Replace with something better for layer & other GL object
@@ -138,7 +155,7 @@
DamageAccumulator mDamageAccumulator;
std::unique_ptr<AnimationContext> mAnimationContext;
- const sp<RenderNode> mRootRenderNode;
+ std::vector< sp<RenderNode> > mRenderNodes;
FrameInfo* mCurrentFrameInfo = nullptr;
// Ring buffer large enough for 2 seconds worth of frames
@@ -148,6 +165,9 @@
FrameInfoVisualizer mProfiler;
std::set<RenderNode*> mPrefetechedLayers;
+
+ // Stores the bounds of the main content.
+ Rect mContentOverdrawProtectionBounds;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 198906c..a47c9ec 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -38,9 +38,11 @@
DrawFrameTask::~DrawFrameTask() {
}
-void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context) {
+void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context,
+ RenderNode* targetNode) {
mRenderThread = thread;
mContext = context;
+ mTargetNode = targetNode;
}
void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
@@ -118,7 +120,7 @@
mContext->processLayerUpdate(mLayers[i].get());
}
mLayers.clear();
- mContext->prepareTree(info, mFrameInfo, mSyncQueued);
+ mContext->prepareTree(info, mFrameInfo, mSyncQueued, mTargetNode);
// This is after the prepareTree so that any pending operations
// (RenderNode tree state, prefetched layers, etc...) will be flushed.
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index ebefcba..68ee897 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -57,7 +57,7 @@
DrawFrameTask();
virtual ~DrawFrameTask();
- void setContext(RenderThread* thread, CanvasContext* context);
+ void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode);
void pushLayerUpdate(DeferredLayerUpdater* layer);
void removeLayerUpdate(DeferredLayerUpdater* layer);
@@ -78,6 +78,7 @@
RenderThread* mRenderThread;
CanvasContext* mContext;
+ RenderNode* mTargetNode = nullptr;
/*********************************************
* Single frame data
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index b838811..f43a769 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -74,7 +74,7 @@
args->thread = &mRenderThread;
args->contextFactory = contextFactory;
mContext = (CanvasContext*) postAndWait(task);
- mDrawFrameTask.setContext(&mRenderThread, mContext);
+ mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
}
RenderProxy::~RenderProxy() {
@@ -91,7 +91,7 @@
SETUP_TASK(destroyContext);
args->context = mContext;
mContext = nullptr;
- mDrawFrameTask.setContext(nullptr, nullptr);
+ mDrawFrameTask.setContext(nullptr, nullptr, nullptr);
// This is also a fence as we need to be certain that there are no
// outstanding mDrawFrame tasks posted before it is destroyed
postAndWait(task);
@@ -461,7 +461,8 @@
staticPostAndWait(task);
}
-CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map, size_t size) {
+CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map,
+ size_t size) {
CanvasContext::setTextureAtlas(*args->thread, args->buffer, args->map, args->size);
args->buffer->decStrong(nullptr);
return nullptr;
@@ -490,6 +491,61 @@
post(task);
}
+CREATE_BRIDGE3(addRenderNode, CanvasContext* context, RenderNode* node, bool placeFront) {
+ args->context->addRenderNode(args->node, args->placeFront);
+ return nullptr;
+}
+
+void RenderProxy::addRenderNode(RenderNode* node, bool placeFront) {
+ SETUP_TASK(addRenderNode);
+ args->context = mContext;
+ args->node = node;
+ args->placeFront = placeFront;
+ post(task);
+}
+
+CREATE_BRIDGE2(removeRenderNode, CanvasContext* context, RenderNode* node) {
+ args->context->removeRenderNode(args->node);
+ return nullptr;
+}
+
+void RenderProxy::removeRenderNode(RenderNode* node) {
+ SETUP_TASK(removeRenderNode);
+ args->context = mContext;
+ args->node = node;
+ post(task);
+}
+
+CREATE_BRIDGE2(drawRenderNode, CanvasContext* context, RenderNode* node) {
+ args->context->prepareAndDraw(args->node);
+ return nullptr;
+}
+
+void RenderProxy::drawRenderNode(RenderNode* node) {
+ SETUP_TASK(drawRenderNode);
+ args->context = mContext;
+ args->node = node;
+ // Be pseudo-thread-safe and don't use any member variables
+ staticPostAndWait(task);
+}
+
+CREATE_BRIDGE5(setContentOverdrawProtectionBounds, CanvasContext* context, int left, int top,
+ int right, int bottom) {
+ args->context->setContentOverdrawProtectionBounds(args->left, args->top, args->right,
+ args->bottom);
+ return nullptr;
+}
+
+void RenderProxy::setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
+ SETUP_TASK(setContentOverdrawProtectionBounds);
+ args->context = mContext;
+ args->left = left;
+ args->top = top;
+ args->right = right;
+ args->bottom = bottom;
+ staticPostAndWait(task);
+}
+
CREATE_BRIDGE1(serializeDisplayListTree, CanvasContext* context) {
args->context->serializeDisplayListTree();
return nullptr;
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index e7356db..046f24a 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -106,6 +106,11 @@
ANDROID_API void serializeDisplayListTree();
+ ANDROID_API void addRenderNode(RenderNode* node, bool placeFront);
+ ANDROID_API void removeRenderNode(RenderNode* node);
+ ANDROID_API void drawRenderNode(RenderNode* node);
+ ANDROID_API void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom);
+
private:
RenderThread& mRenderThread;
CanvasContext* mContext;