RenderNode Demo

目录

    • 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);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/846938.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nginx动静分离配置

在实现nginx动静分离时&#xff0c;需要将静态文件和动态请求进行分离&#xff0c;可以通过以下配置实现&#xff1a; 1. 静态文件配置&#xff1a; location /static/ {root /path/to/static/files;expires 7d;access_log off; }location /media/ {root /path/to/media/file…

【Power Compiler手册】6.反标翻转活动

5.反标翻转活动 可以对设计中的切换活动进行反标,以生成准确的功耗计算。 有关不同类型切换活动信息以及如何在门级设计上进行反标的信息,请参阅以下主题: 反标切换活动的类型使用RTL SAIF文件反标切换活动使用门级SAIF文件反标切换活动反标推断的切换活动使用set_switchin…

TDMQ CKafka 版弹性存储能力重磅上线!

导语 自 2024年5月起&#xff0c;TDMQ CKafka 专业版支持弹性存储能力&#xff0c;这种产品形态下&#xff0c;存储可按需使用、按量付费&#xff0c;一方面降低消费即删除、存储使用波动大场景下的存储成本&#xff0c;另一方面存储空间理论上无穷大。 TDMQ CKafka 版产品能…

Python实用代码片段分享(三)

在今天的博文中&#xff0c;我们将继续分享一些Python编程中非常实用的代码片段。这些代码片段将帮助你更高效地处理常见任务&#xff0c;从字符转换到数据类型检查&#xff0c;应有尽有。 1. ord函数和chr函数 Python的ord()函数可以返回Unicode字符对应的ASCII码值&#xf…

OJ3260最大数组和问题

题目&#xff1a; 小明是一名勇敢的冒险家&#xff0c;他在一次探险途中发现了一组神秘的宝石&#xff0c;这些宝石的价值都不同。但是&#xff0c;他发现这些宝石会随着时间的推移逐渐失去价值&#xff0c;因此他必须在规定的次数内对它们进行处理。 小明想要最大化这些宝石的…

数据结构——二叉树(C语言版)

前言 二叉树是一种非线性的数据结构。二叉搜索树、堆、红黑树等高阶数据结构都是依托于二叉树的基础实现的&#xff0c;所以我们有必要好好研究一下“二叉树”这种数据结构。本文只介绍二叉树的基础及中等用法&#xff0c;笔者能力有限&#xff0c;欠妥当之处欢迎批评指正。 树…

【RS】哨兵系列新网站无法下载的问题及解决办法(Sentinel-2)

最近有些小伙伴留言说哨兵数据无法下载&#xff0c;网站打开后会有一层蒙版&#xff0c;无法选取研究区等信息&#xff0c;今天就跟大家分享一下如何解决这个问题。还知道如何下载的小伙伴可以移步到之前的文章&#xff1a;【RS】欧空局Sentinel-2卫星数据下载(哨兵1、2、3、5P…

海外短剧看剧系统搭建部署,h5/app双端,系统页面一键翻译功能,批量上传素材等功能。

目录 前言&#xff1a; 一、海外短剧系统有是吗功能&#xff1f; 二、海外短剧项目在海外反馈怎么样&#xff1f; 总结&#xff1a; 前言&#xff1a; 海外短剧系统搭建开发&#xff0c;想进军海外短剧市场的&#xff0c;搭建这样一款海外短剧系统是必要的。海外短剧市场规…

ATA-4051C高压功率放大器应用分享:超声波测量液位系统

超声波测量液位是一种非接触式液位测量方法&#xff0c;其原理是利用超声波的传播特性来测量液位。超声波是一种高频机械波&#xff0c;其频率高于人类能够听到的频率&#xff0c;通常在100kHz以上。超声波具有较好的穿透性和反射性&#xff0c;可以在固体、液体和气体中传播&a…

FTP

文章目录 概述主动模式和被动模式的工作过程注意事项 概述 文件传输协议 FTP&#xff08;File Transfer Protocol&#xff09;在 TCP/IP 协议族中属于应用层协议&#xff0c;是文件传输标准。主要功能是向用户提供本地和远程主机之间的文件传输&#xff0c;尤其在进行版本升级…

ThinkBook 14 G6+ IMH(21LD)原厂Win11系统oem镜像下载

lenovo联想笔记本电脑原装出厂Windows11系统安装包&#xff0c; 恢复开箱状态自带预装系统&#xff0c;含恢复重置还原功能 链接&#xff1a;https://pan.baidu.com/s/1WIPNagHrC0wqYC3HIcua9A?pwdhzqg 提取码&#xff1a;hzqg 联想原装出厂系统自带所有驱动、出厂主题壁…

oracle 11g安装补丁

cd /myweb/src mv /myweb/app/oracle/product/11.2.0/dbhome_1/OPatch /myweb/app/oracle/product/11.2.0/dbhome_1/OPatch.old unzip p6880880_112000_Linux-x86-64.zip mv OPatch O R A C L E H O M E e x p o r t P A T H = ORACLE_HOME export PATH=

Zabbix安装:构建高效可靠的Zabbix监控系统

目录 引言 一、zabbix基本介绍 &#xff08;一&#xff09;什么是zabbix &#xff08;二&#xff09;zabbix结构体系 &#xff08;三&#xff09;zabbix监控对象 &#xff08;四&#xff09;zabbix进程 &#xff08;五&#xff09;zabbix监控模式 &#xff08;六&#…

【SQL边干边学系列】01介绍性问题

文章目录 前言介绍性问题1.我们有哪些承运商&#xff1f;2. 从目录表中查询特定字段3.销售代表4.在美国的销售代表5.由特定员工ID下的订单6.供应商和联系人信息 答案1.我们有哪些承运商&#xff1f;2. 从目录表中查询特定字段3.销售代表4.在美国的销售代表5.由特定员工ID下的订…

FFmpeg PCM编码为AAC

使用FFmpeg库把PCM文件编码为AAC文件&#xff0c;FFmpeg版本为4.4.2-0 代码如下&#xff1a; #include <stdio.h> #include <stdlib.h> #include <string.h> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <…

Codes 重新定义 SaaS 模式的研发项目管理平台开源版 4.5.5 发布

一&#xff1a;简介 Codes 重新定义 SaaS 模式 云端认证 程序及数据本地安装 不限功能 30 人免费 Codes 是一个 高效、简洁、轻量的一站式研发项目管理平台。包含需求管理&#xff0c;任务管理&#xff0c;测试管理&#xff0c;缺陷管理&#xff0c;自动化测试&#xff0…

海外短剧的未来展望:创新与发展的方向

随着全球化的加速和互联网技术的飞速发展&#xff0c;海外短剧作为一种新兴的娱乐形式&#xff0c;正逐渐赢得广大观众的喜爱。在这个充满变革的时代&#xff0c;海外短剧面临着前所未有的机遇与挑战。本文将探讨海外短剧未来的创新与发展方向。 一、内容创新&#xff1a;打破传…

Oracle操作扩可变字符长度交易影响分析-较小

使用AI帮助学习知识 以下知识来至AI oracle 一张大表&#xff0c;对可变字符串长度从10扩到20位&#xff0c;oracle底层存储是否会发生变化&#xff0c;先锁表&#xff0c;更新表字典信息&#xff0c;然后会不会重新整理表&#xff0c;在有交易的情况下导致大量交易失效&#…

日常开发坑记录

hutool工具类转换,anInt可能为负数(队列散列需求遇到)long l = RandomUtil.randomLong(0, 9999999999L);Integer anInt = Convert.toInt(l);System

wandb上传整个项目代码

wandb.run.log_code上传整个项目代码 wandb一般只能保存训练代码文件 用了这个函数并且指定文件格式&#xff0c;就能保存指定文件当git来用了 import wandbwandb.init(project"latent-diffusion") wandb.run.log_code(".", include_fnlambda path: path.e…