参考链接: blog.csdn.net/yangxu4536/…
在硬件加速渲染环境中,Android应用程序窗口的UI渲染是分两步进行的。
第一步是构建Display List,发生在应用程序进程的Main Thread中;
第二步是渲染Display List,发生在应用程序进程的Render Thread中。Display List的渲染不是简单地执行绘制命令,而是包含了一系列优化操作,例如绘制命令的合并执行。
Android应用程序窗口的Root Render Node的Display List,包含了Android应用程序窗口所有的绘制命令,因此我们只要对Root Render Node的Display List进行渲染,就可以得到整个Android应用程序窗口的UI。
Android应用程序窗口的Display List的构建是通过Display List Renderer进行的,而渲染是通过Open GL Renderer进行的,如图所示:
Open GL Renderer只作用在Android应用程序窗口的Root Render Node的Display List上,这是因为Root Render Node的Display List包含了Android应用程序窗口所有的绘制命令。
Android应用程序窗口的Display List的渲染是由Render Thread执行的,不过是由Main Thread通知Render Thread执行的,如图所示:
Main Thread通过向Render Thread的TaskQueue添加一个drawFrame任务来通知Render Thread渲染Android应用程序窗口的UI。
Android应用程序窗口的Display List构建完成之后,Main Thread就马上向Render Thread发出渲染命令,如下所示:
public class ThreadedRenderer extends HardwareRenderer {
......
@Override
void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
......
updateRootDisplayList(view, callbacks);
......
if (attachInfo.mPendingAnimatingRenderNodes != null) {
final int count = attachInfo.mPendingAnimatingRenderNodes.size();
for (int i = 0; i < count; i++) {
registerAnimatingRenderNode(
attachInfo.mPendingAnimatingRenderNodes.get(i));
}
attachInfo.mPendingAnimatingRenderNodes.clear();
// We don't need this anymore as subsequent calls to
// ViewRootImpl#attachRenderNodeAnimator will go directly to us.
attachInfo.mPendingAnimatingRenderNodes = null;
}
int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
recordDuration, view.getResources().getDisplayMetrics().density);
if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
attachInfo.mViewRootImpl.invalidate();
}
}
......
}
ThreadedRenderer类的成员函数draw主要执行三个操作:
- 调用成员函数updateRootDisplayList构建或者更新应用程序窗口的Root Render Node的Display List。
- 调用成员函数registerAnimationRenderNode注册应用程序窗口动画相关的Render Node。
- 调用成员函数nSyncAndDrawFrame渲染应用程序窗口的Root Render Node的Display List。
其中,第一个操作在前面Android应用程序UI硬件加速渲染的Display List构建过程分析一文已经分析,第二个操作在接下来的一篇文章中分析,这篇文章主要关注第三个操作,即应用程序窗口的Root Render Node的Display List的渲染过程,即ThreadedRenderer类的成员函数nSyncAndDrawFrame的实现。
ThreadedRenderer类的成员函数nSyncAndDrawFrame是一个JNI函数,由Native层的函数android_view_ThreadedRenderer_syncAndDrawFrame实现,如下所示:
static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
jlong proxyPtr, jlong frameTimeNanos, jlong recordDuration, jfloat density) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
return proxy->syncAndDrawFrame(frameTimeNanos, recordDuration, density);
}
参数proxyPtr描述的是一个RenderProxy对象,这里调用它的成员函数syncAndDrawFrame渲染应用程序窗口的Display List。
RenderProxy类的成员函数syncAndDrawFrame的实现如下所示:
int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
float density) {
mDrawFrameTask.setDensity(density);
return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos);
}
RenderProxy类的成员变量mDrawFrameTask指向的是一个DrawFrameTask对象。在前面Android应用程序UI硬件加速渲染环境初始化过程分析一文提到,这个DrawFrameTask对象描述的是一个用来执行渲染任务的Task,这里调用它的成员函数drawFrame渲染应用程序窗口的下一帧,也就是应用程序窗口的Display List。
DrawFrameTask的成员函数drawFrame的实现如下所示:
int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos) {
......
mSyncResult = kSync_OK;
......
postAndWait();
......
return mSyncResult;
}
DrawFrameTask的成员函数drawFrame最主要的操作就是调用另外一个成员函数postAndWait往Render Thread的Task Queue抛一个消息,并且进入睡眠状态,等待Render Thread在合适的时候唤醒。
DrawFrameTask的成员函数postAndWait的实现如下所示:
void DrawFrameTask::postAndWait() {
AutoMutex _lock(mLock);
mRenderThread->queue(this);
mSignal.wait(mLock);
}
由于DrawFrameTask类描述的就是一个可以添加到Render Thread的Task Queue的Task,因此DrawFrameTask的成员函数postAndWait就将当前正在处理的DrawFrameTask对象添加到由成员变量mRenderThread描述的Render Thread的Task Queue,并且在另外一个成员变量mSignal描述的一个条件变量上进行等待。
从前面Android应用程序UI硬件加速渲染环境初始化过程分析一文可以知道,添加到Render Thread的Task Queue的Task被处理时,它的成员函数run就会被调用,因此接下来DrawFrameTask类的成员函数run就会被调用,它的实现如下所示:
void DrawFrameTask::run() {
......
bool canUnblockUiThread;
bool canDrawThisFrame;
{
TreeInfo info(TreeInfo::MODE_FULL, mRenderThread->renderState());
canUnblockUiThread = syncFrameState(info);
canDrawThisFrame = info.out.canDrawThisFrame;
}
// Grab a copy of everything we need
CanvasContext* context = mContext;
// From this point on anything in "this" is *UNSAFE TO ACCESS*
if (canUnblockUiThread) {
unblockUiThread();
}
if (CC_LIKELY(canDrawThisFrame)) {
context->draw();
}
if (!canUnblockUiThread) {
unblockUiThread();
}
}
要理解这个函数首先要理解应用程序进程的Main Thread和Render Thread是如何协作的。从前面的分析可以知道,Main Thread请求Render Thread执行Draw Frame Task的时候,不能马上返回,而是进入等待状态。等到Render Thread从Main Thread同步完绘制所需要的信息之后,Main Thread才会被唤醒。
那么,Render Thread要从Main Thread同步什么信息呢?原来,Main Thread和Render Thread都各自维护了一份应用程序窗口视图信息。各自维护了一份应用程序窗口视图信息的目的,就是为了可以互不干扰,进而实现最大程度的并行。其中,Render Thread维护的应用程序窗口视图信息是来自于Main Thread的。因此,当Main Thread维护的应用程序窗口信息发生了变化时,就需要同步到Render Thread去。
应用程序窗口的视图信息包含图1所示的各个Render Node的Display List、Property以及Display List引用的Bitmap。在RenderNode类中,有六个成员变量是与Display List和Property相关的,如下所示:
class RenderNode : public VirtualLightRefBase {
public:
......
ANDROID_API void setStagingDisplayList(DisplayListData* newData);
......
const RenderProperties& stagingProperties() {
return mStagingProperties;
}
......
private:
......
uint32_t mDirtyPropertyFields;
RenderProperties mProperties;
RenderProperties mStagingProperties;
bool mNeedsDisplayListDataSync;
// WARNING: Do not delete this directly, you must go through deleteDisplayListData()!
DisplayListData* mDisplayListData;
DisplayListData* mStagingDisplayListData;
......
};
其中,成员变量mStagingProperties描述的Render Properties和成员变量mStagingDisplayListData描述的Display List Data由Main Thread维护,而成员变量mProperties描述的Render Properties和成员变量mDisplayListData描述的Display List Data由Render Thread维护。
这一点可以从前面Android应用程序UI硬件加速渲染的Display List构建过程分析一文看出。当Main Thread构建完成应用程序窗口的Display List之后,就会调用RenderNode类的成员函数setStagingDisplayList将其设置到Root Render Node的成员变量mStagingDisplayListData中去。而当应用程序窗口某一个View的Property发生变化时,就会调用RenderNode类的成员函数mutateStagingProperties获得成员变量mStagingProperties描述的Render Properties,进而修改相应的Property。
当Main Thread维护的Render Properties发生变化时,成员变量mDirtyPropertyFields的值就不等于0,其中不等于0的位就表示是哪一个具体的Property发生了变化,而当Main Thread维护的Display List Data发生变化时,成员变量mNeedsDisplayListDataSync的值就等于true,表示要从Main Thread同步到Render Thread。
另外,在前面Android应用程序UI硬件加速渲染的Display List构建过程分析一文分析将一个Bitmap绘制命令转化为一个DrawBitmapOp记录在Display List时,Bitmap会被增加一个引用,如下所示:
status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
bitmap = refBitmap(bitmap);
paint = refPaint(paint);
addDrawOp(new (alloc()) DrawBitmapOp(bitmap, paint));
return DrawGlInfo::kStatusDone;
}
参数bitmap描述的SkBitmap通过调用DisplayListRenderer类的成员函数refBitmap进行使用,它的实现如下所示:
class ANDROID_API DisplayListRenderer: public StatefulBaseRenderer {
public:
......
inline const SkBitmap* refBitmap(const SkBitmap* bitmap) {
......
mDisplayListData->bitmapResources.add(bitmap);
mCaches.resourceCache.incrementRefcount(bitmap);
return bitmap;
}
......
};
DisplayListRenderer类的成员函数refBitmap在增加参数bitmap描述的一个SkBitmap的引用计数之前,会将它保存在成员变量mDisplayListData指向的一个DisplayListData对象的成员变量bitmapResources描述的一个Vector中。
上述情况是针对调用GLES20Canvas类的以下成员函数drawBitmap绘制一个Bitmap时发生的情况:
class GLES20Canvas extends HardwareCanvas {
......
@Override
public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
throwIfCannotDraw(bitmap);
final long nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
}
......
}
我们还可以调用GLES20Canvas类的另外一个重载版本的成员函数drawBitmap绘制一个Bitmap,如下所示:
class GLES20Canvas extends HardwareCanvas {
......
@Override
public void drawBitmap(int[] colors, int offset, int stride, float x, float y,
int width, int height, boolean hasAlpha, Paint paint) {
......
final long nativePaint = paint == null ? 0 : paint.mNativePaint;
nDrawBitmap(mRenderer, colors, offset, stride, x, y,
width, height, hasAlpha, nativePaint);
}
......
}
GLES20Canvas类这个重载版本的成员函数drawBitmap通过一个int数组来指定要绘制的Bitmap。这个int数组是由应用程序自己管理的,并且会被封装成一个SkBitmap,最终由DisplayListRenderer类的成员函数drawBitmapData将该Bitmap绘制命令封装成一个DrawBitmapDataOp记录在Display List中,如下所示:
status_t DisplayListRenderer::drawBitmapData(const SkBitmap* bitmap, const SkPaint* paint) {
bitmap = refBitmapData(bitmap);
paint = refPaint(paint);
addDrawOp(new (alloc()) DrawBitmapDataOp(bitmap, paint));
return DrawGlInfo::kStatusDone;
}
DisplayListRenderer类的成员函数drawBitmapData通过另外一个成员函数refBitmapData来增加参数bitmap描述的SkBitmap的引用,如下所示:
class ANDROID_API DisplayListRenderer: public StatefulBaseRenderer {
public:
......
inline const SkBitmap* refBitmapData(const SkBitmap* bitmap) {
mDisplayListData->ownedBitmapResources.add(bitmap);
mCaches.resourceCache.incrementRefcount(bitmap);
return bitmap;
}
......
};