目录
- Demo
- 一、帧绘制流程
- 1、RenderProxy::syncAndDrawFrame
- 2、DrawFrameTask::drawFrame
- 3、CanvsContext::draw
- 4、SkiaOpenGLPipeline::draw(真正开始绘制)
- 5、RenderNodeDrawable::onDraw
- 6、DisplayList::draw
- 二、RenderNode传递过程
- 1、SkiaRecordingCanvas
- 2、RenderNode
Demo
int left = 0;
int right = 640;
int top = 0;
int bottom = 480;
sp<Surface> surface = xxxx;//创建RenderNode
sp<RenderNode> node = new RenderNode();
RenderProperties& props = node->mutateStagingProperties();
props.setLeftTopRightBottom(left, top, right, bottom);
node->setPropertyFieldsDirty(0xFFFFFFFF);std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(width, height));
//canvas绘制
...
node->setStagingDisplayList(canvas.finishRecording());
node->setPropertyFieldsDirty(0xFFFFFFFF);//创建RenderProxy
ContextFactory factory;
std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode.get(), &factory));
proxy->loadSystemProperties();
proxy->setSurface(surface.get());
//设置绘制边距,即绘制区域大小
proxy->setContentDrawBounds(left, top, right, bottom);proxy->syncAndDrawFrame();
proxy->resetProfileInfo();
proxy->fence();
一、帧绘制流程
1、RenderProxy::syncAndDrawFrame
实际调用的是DrawFrameTask::drawFrame()方法。
//hwui/renderthread/RenderProxy.cpp
int RenderProxy::syncAndDrawFrame() {return mDrawFrameTask.drawFrame();
}
2、DrawFrameTask::drawFrame
最终调用到DrawFrameTask::run方法,在run方法内调用CanvasContext::draw方法。
//hwui/rednerthread/DrawFrameTask.cpp
int DrawFrameTask::drawFrame() {mSyncResult = SyncResult::OK;postAndWait();return mSyncResult;
}void DrawFrameTask::postAndWait() {AutoMutex _lock(mLock);mRenderThread->queue().post([this]() { run(); });mSignal.wait(mLock);
}void DrawFrameTask::run() {CanvasContext* context = mContext;dequeueBufferDuration = context->draw();
}
3、CanvsContext::draw
CanvasContext::draw方法将自己的RenderNode数组传递给SkiaOpenGLPipline进行绘制,待绘制完成后,调用其swapBuffers进行送显。
//hwui/renderthread/CanvasContext.cpp
nsecs_t CanvasContext::draw() {SkRect dirty;Frame frame = mRenderPipeline->getFrame();SkRect windowDirty = computeDirtyRect(frame, &dirty);bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes,&(profiler()));waitOnFences();bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap);mIsDirty = false;
}
4、SkiaOpenGLPipeline::draw(真正开始绘制)
创建SkSurface,并获取SkCanvas对象,绘制的准备工作完成。然后使用Render、SkCanvas构建RenderNodeDrawable对象,并调用其draw方法进行绘制。
//hwui/pipline/skia/SkiaOpenGLPipline.cpp
bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,const LightGeometry& lightGeometry,LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds,bool opaque, const LightInfo& lightInfo,const std::vector<sp<RenderNode>>& renderNodes,FrameInfoVisualizer* profiler) {SkColorType colorType = getSurfaceColorType();// setup surface for fbo0GrGLFramebufferInfo fboInfo;fboInfo.fFBOID = 0;// Note: The default preference of pixel format is RGBA_8888, when other// pixel format is available, we should branch out and do more check.fboInfo.fFormat = GL_RGBA8;GrBackendRenderTarget backendRT(frame.width(), frame.height(), 0, STENCIL_BUFFER_SIZE, fboInfo);SkSurfaceProps props(0, kUnknown_SkPixelGeometry);sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(), backendRT, this->getSurfaceOrigin(), colorType, mSurfaceColorSpace, &props));LightingInfo::updateLighting(lightGeometry, lightInfo);//调用renderFrame进行渲染renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, SkMatrix::I());surface->flushAndSubmit();layerUpdateQueue->clear();return true;
}void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,const std::vector<sp<RenderNode>>& nodes, bool opaque,const Rect& contentDrawBounds, sk_sp<SkSurface> surface,const SkMatrix& preTransform) {// Initialize the canvas for the current frame, that might be a recording canvas if SKP// capture is enabled.SkCanvas* canvas = tryCapture(surface.get(), nodes[0].get(), layers);// draw all layers up frontrenderLayersImpl(layers, opaque);renderFrameImpl(clip, nodes, opaque, contentDrawBounds, canvas, preTransform);endCapture(surface.get());if (CC_UNLIKELY(Properties::debugOverdraw)) {renderOverdraw(clip, nodes, contentDrawBounds, surface, preTransform);}
}void SkiaPipeline::renderFrameImpl(const SkRect& clip,const std::vector<sp<RenderNode>>& nodes, bool opaque,const Rect& contentDrawBounds, SkCanvas* canvas,const SkMatrix& preTransform) {SkAutoCanvasRestore saver(canvas, true);canvas->concat(preTransform);if (1 == nodes.size()) {if (!nodes[0]->nothingToDraw()) {RenderNodeDrawable root(nodes[0].get(), canvas);root.draw(canvas);}} else if (0 == nodes.size()) {// nothing to draw} else {// It there are multiple render nodes, they are laid out as follows:// #0 - backdrop (content + caption)// #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop)// #2 - additional overlay nodes// 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 draw the// content// cropped to the backdrop (since that indicates a shrinking of the window).//// Additional nodes will be drawn on top with no particular clipping semantics.// Usually the contents bounds should be mContentDrawBounds - however - we will// move it towards the fixed edge to give it a more stable appearance (for the moment).// If there is no content bounds we ignore the layering as stated above and start with 2.// Backdrop bounds in render target spaceconst Rect backdrop = nodeBounds(*nodes[0]);// Bounds that content will fill in render target space (note content node bounds may be// bigger)Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight());content.translate(backdrop.left, backdrop.top);if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) {// Content doesn't entirely overlap backdrop, so fill around content (right/bottom)// Note: in the future, if content doesn't snap to backdrop's left/top, this may need to// also fill left/top. Currently, both 2up and freeform position content at the top/left// of// the backdrop, so this isn't necessary.RenderNodeDrawable backdropNode(nodes[0].get(), canvas);if (content.right < backdrop.right) {// draw backdrop to right side of contentSkAutoCanvasRestore acr(canvas, true);canvas->clipRect(SkRect::MakeLTRB(content.right, backdrop.top, backdrop.right,backdrop.bottom));backdropNode.draw(canvas);}if (content.bottom < backdrop.bottom) {// draw backdrop to bottom of content// Note: bottom fill uses content left/right, to avoid overdrawing left/right fillSkAutoCanvasRestore acr(canvas, true);canvas->clipRect(SkRect::MakeLTRB(content.left, content.bottom, content.right,backdrop.bottom));backdropNode.draw(canvas);}}RenderNodeDrawable contentNode(nodes[1].get(), canvas);if (!backdrop.isEmpty()) {// content node translation to catch up with backdropfloat dx = backdrop.left - contentDrawBounds.left;float dy = backdrop.top - contentDrawBounds.top;SkAutoCanvasRestore acr(canvas, true);canvas->translate(dx, dy);const SkRect contentLocalClip =SkRect::MakeXYWH(contentDrawBounds.left, contentDrawBounds.top,backdrop.getWidth(), backdrop.getHeight());canvas->clipRect(contentLocalClip);contentNode.draw(canvas);} else {SkAutoCanvasRestore acr(canvas, true);contentNode.draw(canvas);}// remaining overlay nodes, simply deferfor (size_t index = 2; index < nodes.size(); index++) {if (!nodes[index]->nothingToDraw()) {SkAutoCanvasRestore acr(canvas, true);RenderNodeDrawable overlayNode(nodes[index].get(), canvas);overlayNode.draw(canvas);}}}
}
5、RenderNodeDrawable::onDraw
从RenderNode中获取DisplayList,最终会调用的DisplayList::draw方法。
//hwui/pipeline/skia/RenderNodeDrawable.cpp
void RenderNodeDrawable::onDraw(SkCanvas* canvas) {// negative and positive Z order are drawn out of order, if this render node drawable is in// a reordering sectionif ((!mInReorderingSection) || MathUtils::isZero(mRenderNode->properties().getZ())) {this->forceDraw(canvas);}
}void RenderNodeDrawable::forceDraw(SkCanvas* canvas) const {RenderNode* renderNode = mRenderNode.get();SkiaDisplayList* displayList = renderNode->getDisplayList().asSkiaDl();SkAutoCanvasRestore acr(canvas, true);const RenderProperties& properties = this->getNodeProperties();// pass this outline to the children that may clip backward projected nodesdisplayList->mProjectedOutline = displayList->containsProjectionReceiver() ? &properties.getOutline() : nullptr;if (!properties.getProjectBackwards()) {drawContent(canvas);if (mProjectedDisplayList) {canvas->setMatrix(mProjectedDisplayList->mParentMatrix);drawBackwardsProjectedNodes(canvas, *mProjectedDisplayList);}}displayList->mProjectedOutline = nullptr;
}void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {RenderNode* renderNode = mRenderNode.get();SkiaDisplayList* displayList = mRenderNode->getDisplayList().asSkiaDl();displayList->mParentMatrix = canvas->getTotalMatrix();TransformCanvas transformCanvas(canvas, SkBlendMode::kClear);displayList->draw(&transformCanvas);
}
6、DisplayList::draw
DisplayList::draw实际上调用的是DisplayList::draw方法,内部调用map方法,依次执行添加DisplayListData中的任务。
//hwui/RecordingCanvas.cpp
void DisplayListData::draw(SkCanvas* canvas) const {SkAutoCanvasRestore acr(canvas, false);this->map(draw_fns, canvas, canvas->getTotalMatrix());
}template <typename Fn, typename... Args>
inline void DisplayListData::map(const Fn fns[], Args... args) const {auto end = fBytes.get() + fUsed;for (const uint8_t* ptr = fBytes.get(); ptr < end;) {auto op = (const Op*)ptr;auto type = op->type;auto skip = op->skip;if (auto fn = fns[type]) { // We replace no-op functions with nullptrsfn(op, args...); // to avoid the overhead of a pointless call.}ptr += skip;}
}
二、RenderNode传递过程
1、SkiaRecordingCanvas
该类的接口几乎与Canvas对齐,但执行drawLine、drawRect时并没有直接的绘制,而是将绘制生成一个个的绘制命令存储在DisplayList。
// hwui/pipeline/skia/SkiaRecordingCanvas.cpp
std::unique_ptr<SkiaDisplayList> SkiaRecordingCanvas::finishRecording() {// close any existing chunks if necessaryenableZ(false);mRecorder.restoreToCount(1);return std::move(mDisplayList);
}
//直接将DisplayList存储到RenderNode中
void SkiaRecordingCanvas::finishRecording(uirenderer::RenderNode* destination) {destination->setStagingDisplayList(uirenderer::DisplayList(finishRecording()));
}
2、RenderNode
- RenderNode内部存储DisplayList对象,由setStagingDisplayList方法接受外部的DisplayList数据
- DisplayList在RenderNodeDrawable::drawContent中取出使用
// hwui/RenderNode.cpp
void RenderNode::setStagingDisplayList(DisplayList&& newData) {mValid = newData.isValid();mNeedsDisplayListSync = true;mStagingDisplayList = std::move(newData);
}void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) {// Make sure we inc first so that we don't fluctuate between 0 and 1,// which would thrash the layer cacheif (mStagingDisplayList) {mStagingDisplayList.updateChildren([](RenderNode* child) { child->incParentRefCount(); });}deleteDisplayList(observer, info);//将mStagingDisplayList暂存数据同步到mDisplayList中mDisplayList = std::move(mStagingDisplayList);if (mDisplayList) {WebViewSyncData syncData {.applyForceDark = info && !info->disableForceDark};mDisplayList.syncContents(syncData);handleForceDark(info);}
}