目录
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 fbo0
GrGLFramebufferInfo 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 front
renderLayersImpl(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 space
const 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 content
SkAutoCanvasRestore 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 fill
SkAutoCanvasRestore 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 backdrop
float 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 defer
for (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 section
if ((!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 nodes
displayList->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 nullptrs
fn(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 necessary
enableZ(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 cache
if (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);
}
}