硬件渲染-数据同步
Java层,ThreadedRenderer的draw方法,首先记录树形结构每个视图节点绘制操作,底层DisplayListData保存,然后JNI#方法,nSyncAndDrawFrame同步。
根据指针,找到底层RenderProxy代理。
RenderProxy#syncAndDrawFrame方法。
int RenderProxy::syncAndDrawFrame() {
return mDrawFrameTask.drawFrame();
}
DrawFrameTask任务,绘制帧
唤醒渲染线程
int DrawFrameTask::drawFrame() {
mSyncResult = kSync_OK;
mSyncQueued = systemTime(CLOCK_MONOTONIC);
postAndWait();
return mSyncResult;
}
主线程等待wait。
void DrawFrameTask::postAndWait() {
AutoMutex _lock(mLock);
mRenderThread->queue(this);
mSignal.wait(mLock);
}
渲染线程,RenderThread继承Thread,单例,内部任务队列TaskQueue,封装RenderTask任务操作。DrawFrameTask继承RenderTask。
void RenderThread::queue(RenderTask* task) {
AutoMutex _lock(mLock);
mQueue.queue(task);
if (mNextWakeup && task->mRunAt < mNextWakeup) {
mNextWakeup = 0;
mLooper->wake();
}
}
插入队列TaskQueue,RenderThread的queue方法。判断下次唤醒的时间。唤醒渲染线程。
渲染线程利用Looper休眠。
RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
... {
mFrameCallbackTask = new DispatchFrameCallbacks(this);
mLooper = new Looper(false);
run("RenderThread");
}
构造方法,Looper创建,线程启动,threadLoop循环。
bool RenderThread::threadLoop() {
setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
initThreadLocals();
int timeoutMillis = -1;
for (;;) {//循环
int result = mLooper->pollOnce(timeoutMillis);
nsecs_t nextWakeup;
while (RenderTask* task = nextTask(&nextWakeup)) {
task->run();
}
if (nextWakeup == LLONG_MAX) {
timeoutMillis = -1;
} else {
nsecs_t timeoutNanos = nextWakeup - systemTime(SYSTEM_TIME_MONOTONIC);
timeoutMillis = nanoseconds_to_milliseconds(timeoutNanos);
if (timeoutMillis < 0) {
timeoutMillis = 0;
}
}
...
}
return false;
}
渲染线程在Looper#pollOnce处休眠,当主线程插入任务时,Looper#wake唤醒渲染线程。
队列的RenderTask任务依次执行。
总结:
渲染线程专注于与gpu交互,而主线程负责App视图绘制记录,在App,每一次树形视图绘制都是一个渲染任务。
那么,这一次渲染任务到底做什么呢?
一次渲染任务
渲染线程,两个功能。
1:同步帧状态,syncFrameState。
2:绘制,draw。
void DrawFrameTask::run() {
bool canUnblockUiThread;
bool canDrawThisFrame;
{
TreeInfo info(TreeInfo::MODE_FULL, mRenderThread->renderState());
canUnblockUiThread = syncFrameState(info);
canDrawThisFrame = info.out.canDrawThisFrame;
}
//任务内部的CanvasContext
CanvasContext* context = mContext;
if (canUnblockUiThread) {//唤醒主线程
unblockUiThread();
}
if (CC_LIKELY(canDrawThisFrame)) {
context->draw();
}
if (!canUnblockUiThread) {
unblockUiThread();
}
}
整流程图如下所示。
硬件渲染一次任务整体流程图.png
功能点已在图中标记,下面分别分析。
同步帧状态
一个任务,就是一帧数据,syncFrameState方法同步帧的状态和数据。
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
mRenderThread->timeLord().vsyncReceived(vsync);
mContext->makeCurrent();
Caches::getInstance().textureCache.resetMarkInUse(mContext);
//第一步,遍历DeferredLayerUpdater数组Vector
//触发CanvasContext的processLayerUpdate方法。
for (size_t i = 0; i < mLayers.size(); i++) {
mContext->processLayerUpdate(mLayers[i].get());
}
//每次push结束后清理数组
mLayers.clear();
//第二步
mContext->prepareTree(info, mFrameInfo, mSyncQueued);
if (info.out.hasAnimations) {
if (info.out.requiresUiRedraw) {
mSyncResult |= kSync_UIRedrawRequired;
}
}
//如果返回的是false,说明我们耗尽了textureCache的空间
return info.prepareTextures;
}
第一步 遍历Layers数组
分析一下什么样的视图会将Layer加入DrawFrameTask的Layers数组?
DrawFrameTask#pushLayerUpdate负责添加元素。追溯到Java层。
ThreadedRenderer#pushLayerUpdate方法。
@Override
void pushLayerUpdate(HardwareLayer layer) {
nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
}
通过JNI#方法,触发RenderProxy#pushLayerUpdate方法。
void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) {
mDrawFrameTask.pushLayerUpdate(layer);
}
因此,向Layers数组添加元素,由Java层ThreadedRenderer的pushLayerUpdate发起的。那么,这是什么样的视图呢?
以下两个HardwareLayer#均触发ThreadedRenderer#pushLayerUpdate方法。**
public void setSurfaceTexture(SurfaceTexture surface) {
nSetSurfaceTexture(mFinalizer.get(), surface, false);
mRenderer.pushLayerUpdate(this);//mRenderer即ThreadedRenderer。
}
public void updateSurfaceTexture() {
nUpdateSurfaceTexture(mFinalizer.get());
mRenderer.pushLayerUpdate(this);
}
TextureView视图有一个HardwareLayer,HardwareLayer对应底层DeferredLayerUpdater,封装真正的Layer,因此,可以得出结论。
TextureView视图刷新时,利用ThreadedRenderer将HardwareLayer底层DeferredLayerUpdater加入DrawFrameTask的数组,第一步遍历数组针对TextureView视图。
普通视图,数组是空。
针对TextureView视图处理
遍历每一个DeferredLayerUpdater,CanvasContext#processLayerUpdate方法。
void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater) {
bool success = layerUpdater->apply();
if (layerUpdater->backingLayer()->deferredUpdateScheduled) {
//mCanvas是OpenGLRenderer。
mCanvas->pushLayerUpdate(layerUpdater->backingLayer());
}
}
HardwareLayer结构图如下。
HardwareLayer结构图.jpg 由代码可知,将deferredUpdateScheduled标志的Layer加入OpenGLRenderer的Layer数组。此标志从哪里来呢?TextureView视图的Layer有此标志吗?
先看一下LayerType。上层配置三种。
LAYER_TYPE_NONE。
LAYER_TYPE_SOFTWARE。
LAYER_TYPE_HARDWARE。
底层对应None = 0,Software = 1,RenderLayer = 2。
View#setLayerType设置LayerType,TextureView重写此方法,忽略类型,默认不需要,LAYER_TYPE_NONE。
从源码中可知,Layer#updateDeferred方法设置deferredUpdateScheduled标志,Layer渲染后重置。此方法仅在RenderNode#pushLayerUpdate中调用,由RenderNode内部Layer触发。
LayerType是RenderLayer。
因此:上层专门设置LAYER_TYPE_HARDWARE的视图才会在RenderNode内部创建Layer,并有此标志。目前看来,TextureView视图底层Layer没有deferredUpdateScheduled标志,不会被加入OpenGLRenderer的Layer数组。
总结
第一步,处理和TextureView视图相关,底层Layer,普通视图无HardwareLayer,因此,DrawFrameTask数组是空。
第二步 准备ViewTree的数据
void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
int64_t syncQueued) {
mRenderThread.removeFrameCallback(this);
//当前帧FrameInfo无SkippedFrame标志
//说明之前的帧没有被跳过,mCurrentFrameInfo指向缓冲区新节点。
if (!wasSkipped(mCurrentFrameInfo)) {
mCurrentFrameInfo = &mFrames.next();//RingBuffer<FrameInfo, 120>循环缓冲区
}
//若之前的帧被跳过,继续用当前指向的节点进行初始化。
mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo);
mCurrentFrameInfo->set(FrameInfoIndex::SyncQueued) = syncQueued;
mCurrentFrameInfo->markSyncStart();
//初始化info信息
info.damageAccumulator = &mDamageAccumulator;
info.renderer = mCanvas;
info.canvasContext = this;
mAnimationContext->startFrame(info.mode);
//底层根节点RenderNode准备,树形结构遍历
mRootRenderNode->prepareTree(info);
mAnimationContext->runRemainingAnimations(info);
//不存在mNativeWindow退出,无法绘制该帧canDrawThisFrame设false
int runningBehind = 0;
mNativeWindow->query(mNativeWindow.get(),
NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
info.out.canDrawThisFrame = !runningBehind;
if (!info.out.canDrawThisFrame) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
}
if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
if (!info.out.requiresUiRedraw) {
mRenderThread.postFrameCallback(this);
}
}
}
CanvasContext的底层根节点RootRenderNode,在CanvasContext构造方法初始化。在上层ThreadedRenderer保存指针,RootRenderNode继承RenderNode。
void RenderNode::prepareTree(TreeInfo& info) {
bool functorsNeedLayer = Properties::debugOverdraw;
prepareTreeImpl(info, functorsNeedLayer);
}
从根节点开始,递归遍历。
void RenderNode::prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer) {
info.damageAccumulator->pushTransform(this);
//还有一种模式MODE_RT_ONLY
if (info.mode == TreeInfo::MODE_FULL) {
//属性改变,赋值给mProperties。
pushStagingPropertiesChanges(info);
}
bool willHaveFunctor = false;
if (info.mode == TreeInfo::MODE_FULL && mStagingDisplayListData) {
willHaveFunctor = !mStagingDisplayListData->functors.isEmpty();
} else if (mDisplayListData) {
willHaveFunctor = !mDisplayListData->functors.isEmpty();
}
bool childFunctorsNeedLayer = mProperties.prepareForFunctorPresence(
willHaveFunctor, functorsNeedLayer);
prepareLayer(info, animatorDirtyMask);
if (info.mode == TreeInfo::MODE_FULL) {
//赋值给mDisplayListData。
pushStagingDisplayListChanges(info);
}
prepareSubTree(info, childFunctorsNeedLayer, mDisplayListData);
pushLayerUpdate(info);
info.damageAccumulator->popTransform();
}
prepareTree准备树形结构的流程如下图所示。
CanvasContext的prepareTree准备树形结构的流程.jpg 关注两个方法。prepareSubTree方法,递归遍历子节点。
pushLayerUpdate方法,RenderNode节点的内部Layer处理。
void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer,
DisplayListData* subtree) {
//该节点的DisplayListData存在
if (subtree) {
//获取TextureCahe对象
TextureCache& cache = Caches::getInstance().textureCache;
//数据中Functor集合
info.out.hasFunctors |= subtree->functors.size();
for (size_t i = 0; info.prepareTextures && i <
subtree->bitmapResources.size(); i++) {
//若有一个texture失败,是0,赋值prepareTextures,不再遍历。
info.prepareTextures = cache.prefetchAndMarkInUse(
info.canvasContext, subtree->bitmapResources[i]);
}
for (size_t i = 0; i < subtree->children().size(); i++) {
DrawRenderNodeOp* op = subtree->children()[i];
RenderNode* childNode = op->mRenderNode;
info.damageAccumulator->pushTransform(&op->mTransformFromParent);
bool childFunctorsNeedLayer = functorsNeedLayer
|| op->mRecordedWithPotentialStencilClip;
childNode->prepareTreeImpl(info, childFunctorsNeedLayer);
info.damageAccumulator->popTransform();
}
}
}
- 遍历SkBitmap数组,查看每个Bitmap是否准备好纹理。
SkBitmap数组元素从哪里来呢?
画布绘制Bitmap,DisplayListCanvas#drawBitmap方法。
void DisplayListCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
//refBitmap会向mDisplayListData的bitmapResources数组中保存一份localBitmap
bitmap = refBitmap(*bitmap);
paint = refPaint(paint);
addDrawOp(new (alloc()) DrawBitmapOp(bitmap, paint));
}
inline const SkBitmap* refBitmap(const SkBitmap& bitmap) {
SkBitmap* localBitmap = new (alloc()) SkBitmap(bitmap);
alloc().autoDestroy(localBitmap);
mDisplayListData->bitmapResources.push_back(localBitmap);
return localBitmap;
}
增加一个DrawBitmapOp操作,向数组保存一个SkBitmap。
-
prefetchAndMarkInUse方法,返回Bitmap的Texture。
根据piexlRef的stableId在LruCache缓存查找Texture,若未查到,判断是否可创建,若加上Bitmap大小后大于缓存设计的大小,删除未使用的旧Texture,若仍大于最大值,无法创建新Texture,返回null。
创建Texture时,先glGenTextures,生成纹理对象索引,保存在texture的id,bindTexture绑定,glTexImage2D生成2D纹理将绑定Bitmap像素地址。最后,新Texture存入LruCache缓存。
bool TextureCache::prefetchAndMarkInUse(void* ownerToken, const SkBitmap* bitmap) {
Texture* texture = getCachedTexture(bitmap, AtlasUsageType::Use);
if (texture) {//texture不为0
texture->isInUse = ownerToken;
}
return texture;
}
若所有Texture存在,在syncFrameState结束后,返回info.prepareTextures标志true,它会影响唤醒主线程的时机。
- 遍历DisplayListData的DrawRenderNodeOp数组。底层子RenderNode节点。递归prepareTreeImpl方法。
DrawRenderNodeOp数组是如何加入的呢?
追溯到Java层,DisplayListCanvas#drawRenderNode方法将子节点RenderNode写入。底层创建一个DrawRenderNodeOp操作,封装被绘制视图的底层RenderNode节点,将操作加入到DrawRenderNodeOp数组。
RenderNode#pushLayerUpdate方法。
void RenderNode::pushLayerUpdate(TreeInfo& info) {
LayerType layerType = properties().effectiveLayerType();
if (CC_LIKELY(layerType != LayerType::RenderLayer) ||
CC_UNLIKELY(!isRenderable())) {
if (CC_UNLIKELY(mLayer)) {
LayerRenderer::destroyLayer(mLayer);
mLayer = nullptr;
}
return;
}
bool transformUpdateNeeded = false;
if (!mLayer) {
//内部Layer不存在,由LayerRenderer创建。
mLayer = LayerRenderer::createRenderLayer(info.renderState,
getWidth(), getHeight());
applyLayerPropertiesToLayer(info);
damageSelf(info);
transformUpdateNeeded = true;
}
...
if (dirty.intersect(0, 0, getWidth(), getHeight())) {
dirty.roundOut(&dirty);
//初始化Layer的RenderNode,deferredUpdateScheduled标志
//初始化渲染器render为LayerRenderer。
mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop,
dirty.fRight, dirty.fBottom);
}
if (info.renderer && mLayer->deferredUpdateScheduled) {
info.renderer->pushLayerUpdate(mLayer);//push进数组
}
...
}
满足两个条件执行。
LayerType必须是RenderLayer类型(Java层,View#setLayerType方法设置)。
DisplayListData不是空。
否则退出方法。
初始化创建RenderNode内部Layer,TreeInfo的OpenGLRenderer,将该Layer加入OpenGLRenderer的Layer数组,等待OpenGLRenderer#updateLayers更新。
总结
1,从底层根节点RootRenderNode开始,执行prepareTreeImpl方法,prepareSubTree进入子视图,从当前RenderNode的DisplayListData中查找子视图RenderNode节点,递归底层子视图节点RenderNode的prepareTreeImpl方法。
2,底层根节点RootRenderNode并非顶层视图节点,而是上层ThreadedRenderer内部RenderNode对应的底层节点。
3,上层画布触发过drawBitmap时,存在SkBitmap数组。
4,上层设置LAYER_TYPE_HARDWARE的视图,底层RenderNode内部创建一个Layer。
到这里,就结束了根RenderNode的prepareTreeImpl方法,并递归遍历子节点的prepareTreeImpl。继续回到CanvasContext的prepareTree。
接下来是NativeWindow#query方法。
那么何时初始化NativeWindow呢?
追溯到Java层,ViewRootImpl#performTraversals,调用ThreadedRenderer的initialize方法,该方法传入由Wms初始化的Surface。
@Override
boolean initialize(Surface surface) throws OutOfResourcesException {
mInitialized = true;
updateEnabledState(surface);
boolean status = nInitialize(mNativeProxy, surface);
return status;
}
根据上层Surface获取底层NativeWindow。从JNI#触发RenderProxy#initialize方法,到CanvasContext的initialize方法。
bool CanvasContext::initialize(ANativeWindow* window) {
setSurface(window);
mCanvas = new OpenGLRenderer(mRenderThread.renderState());
mCanvas->initProperties();
return true;
}
初始化OpenGLRenderer。
利用NativeWindow创建一个mEglSurface。
void CanvasContext::setSurface(ANativeWindow* window) {
mNativeWindow = window;
...
if (window) {
mEglSurface = mEglManager.createSurface(window);
}
}
NativeWindow是Java层Surface的底层对象。
query方法的目的是查询生产者和消费者缓冲区的运行情况。
NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND的官方解释为是否消费者比生产者对缓冲区的处理落后大于1个缓冲区,runningBehind返回结果,如果runningBehind为false说明未落后,则info.out.canDrawThisFrame置为true,说明该帧可以绘制,否则canDrawThisFrame设为false跳过该帧。
总结:
第二步,prepareTree逻辑是绘制准备,将属性与DisplayList数据交接,DrawRenderNodeOp操作对应节点递归处理,RenderNode必要时初始化Layer,准备NativeWindow查询是否该帧需要跳过。
syncFrameState同步结束,返回info.prepareTextures结果,赋值canUnblockUiThread。
若info.prepareTextures标志,唤醒主线程。Signal#signal()。
否则,说明存在未准备好的Texture。先执行CanvasContext#draw方法,再唤醒主线程。
任重而道远