CocosCreator3.4.2源码渲染流程解读

news2024/11/17 13:30:41

 首先上一张脑图镇楼,有些流程还有待后续补充,但是整个刷新过程差不多都完成了。

在上一个xmind文件地址
https://download.csdn.net/download/m0_37609239/87254684

接下来就正式开始讲代码:

每帧刷新渲染还是从mainloop开始讲,前面的流程可以看
​​​​​​​​​​​​​​CocosCreator 渲染_椰子糖莫莫的博客-CSDN博客_cocoscreator渲染流程

在director.ts中有一个tick函数,这就是每次刷新的入口:

this._compScheduler.startPhase();
组件定时器 调用start,有变量控制只调用一次
_compScheduler.updatePhase(dt)
组件定时器 再调用update
this._systems[i].update(dt);
系统定时器遍历调用 update
this._compScheduler.lateUpdatePhase(dt);
组件定时器 再调用 lateUpdate
this._systems[i].postUpdate(dt);
最后系统定时器在调用postUpdate

这里包含一个组件定时器和一个系统定时器。(当然,中文名字是我自己取的)

顾名思义,这里的组件定时器中存储的就是各个组件的生命周期方法。
start,update和lateUpdate。

系统定时器就是各组件脚本中调用的this.schedule() 方法,见图中Component组件父类
这里的schedule调用的就是director中的系统定时器
所以系统定时器的调用都会在组件定时器调用完毕后执行

this._root!.frameMove(dt);
绘制帧内容,这里就是渲染的入口了
​​​​

在root类中包含了这些内容,我们跳转到 frameMove函数中看

for (let i = 0; i < this._scenes.length; ++i) {
this._scenes[i].removeBatches();
}
-------------------------------------------------------
这里的scene不是指继承自 BaseNode 的场景
而是指renderScene渲染场景
在场景的构造函数里会调用director.root.createScene()
新建渲染场景,并插入到root列表中
这里 首先调用所有scene的 removeBatches 函数
​​​​​​​跳转过去就是
_drawBatch2Ds.clear();
绘制2D批次的清除,为接下来的绘制做准备。

const windows = this._windows;
const cameraList: Camera[] = [];
for (let i = 0; i < windows.length; i++) {
const window = windows[i];
window.extractRenderCameras(cameraList);
}
-------------------------------------------------------
_windows是在root初始化时构建的RenderWindow类实例
这里遍历调用RenderWindow的extractRenderCameras函数

跳转进去

++++++++++++++++++++++++++++++++++++++++++++++++++++++

​​​​​​​public extractRenderCameras (cameras: Camera[]) {
for (let j = 0; j < this._cameras.length; j++) {
const camera = this._cameras[j];
if (camera.enabled) {
    camera.update();
    cameras.push(camera);
}}}
-------------------------------------------------------
在此函数中主要是遍历自身_cameras
如果相机可用就调用update函数
并压入传递进的Camera列表中
-------------------------------------------------------
这里的相机是在scene.Camera初始化的时候调用
RenderWindow.attachCamera() 函数把自己传递给渲染窗口实例的
-------------------------------------------------------
而scene.Camera是在Camera-component的onLoad函数里
调用root的createCamera函数创建并初始化

-------------------------------------------------------
这里的scene.Camera的update方法主要是
在相机自身位置改变或者状态等改变时重新计算各类数据 如矩阵等

+++++++++++++++++++++++++++++++++++++++++++++++++++++​​​

this._device.acquire([legacyCC.game._swapchain]);
-------------------------------------------------------
获取下一个交换链
通常在游戏中会用两种颜色缓存,一种是主缓存(前向缓存),
另一种辅助缓存(后向缓存)。我们把绘制过程画在辅助缓存,
把绘制好的结果(这时就变成主缓存)显示在屏幕上。
在游戏中切换缓存,使得一个用于写入,
一个用于显示输出就可以消除这样的问题。
所以主缓存是显示在屏幕上的,而辅助缓存被用于下一帧的更新。
在计算机图形学中,这种乒乓技术就是双缓存(也叫页面翻转)。

this._batcher.update();
-------------------------------------------------------
_batcher是Batcher2D类的实例,在root的setRenderPipeline中构建
这个update就是所有2d相关界面渲染入口

跳转进去看这里的代码

  • ​​​​​​​for (let i = 0; i < screens.length; ++i) {
    ...
    }
    遍历所有的screen
    这里的screen其实就是添加进来的Canvas,也就是说界面遍历是从canvas开始的
    详情可见Canvas

    下面看中间循环的代码
  • this.walk(screen.node);
    首先调用了walk方法,把canvas的节点传了进去,跳转进去看下
    ​​​​​​​++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    const uiProps = node._uiProps;
    const render = uiProps.uiComp as Renderable2D;
    render.updateAssembler(this);
    _uiProps是节点的变量,包含了节点的变换内容UITransform,还有组件Comp
    uiComp是节点的可渲染组件,一般继承自Renderable2D
    updateAssembler渲染数据组装程序,这个方法会在所有子节点数据组装之前更新并组装当前组件的渲染数据到 UI 的顶点数据缓冲区中。一般在 UI 渲染流程中调用,用于组装所有的渲染数据到顶点数据缓冲区。
    这个渲染组装程序干了啥,跳转进去
    -------------------------------------------------------
    if (this._renderDataFlag) {
    this._assembler!.updateRenderData(this, render);
    this._renderDataFlag = false;
    }
    如果渲染数据修改过就更新
    -------------------------------------------------------
    this._render(render);
    如果节点可渲染就渲染,但是其实canvas是不需要真正渲染的
    但,这个调用其实才是核心,​​​​​​​各可渲染子节点会实现这个过程,这个渲染组装程序就结束了,回到walk接着看
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    updateOpacity(render.renderData, opacity);
    -------------------------------------------------------
    透明度修改
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    if (children.length > 0 && !node._static) {
    for (let i = 0; i < children.length; ++i) {
    const child = children[i];
    this.walk(child, level);
    }
    }
    -------------------------------------------------------
    遍历所有子节点 (深度优先)
    这里又去把子节点重复walk流程,遍历开始了,这里就应该跳出来看真正的可渲染组件了,看到图中的节点构成部分
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


首先看用的最多的精灵,基本属性在编辑器里也能看到,看到这里顺便讲一下creator比cocosstudio好的地方就是加入了材质系统可视化了。我们可以直接编写着色器脚本和材质搭配,直接赋予到组件上。
然后主要看这个_render函数,这个函数就是之前提到的 渲染组装程序 中调用的_render。
那么这个commitComp方法是干啥的呢。

他其实就是之前走过来的_batcher中的方法,这里面会做合批操作,那么哪些组件是可合批的呢,图中展示的很清楚,缓冲区,所属的层,混合模式,纹理值,材质等必须相同,就可以参与合批,也就是加入到同一个渲染命令中。
我们在日常开发中常做的合图操作就是为了满足这里的条件。

 在看到mask遮罩,遮罩就是改变模板缓冲区的功能,一个遮罩就会走两次drawcall。

在看到一个常用的组件,spine动画。

在3.4.2版本中对spine动画的处理非常非常的粗糙,且不说没有各spine动画之间的合批操作了,就连spine动画内部的合批都没有!

可以看到左边的_render函数,在美术小姐姐们做完spine动画后,给到我们的是一张合图,但是这里还是根据spine动画中的数据,美术小姐姐那边有多少层级,这里就有多少次drawcall。

我下载了最新的3.6.2的代码,看到已经对spine动画做了优化,这里的_render方法修改为先把整个渲染数据合批后在走一次drawcall。
还有spine动画之间的合批,看代码里已经支持了,这个就等后续再研究了。

看了几个常用的组件,搞清楚了一些事情,我们回到walk函数中接着看
render.postUpdateAssembler(this);
-------------------------------------------------------
后置渲染数据组装程序,它会在所有子节点的渲染数据组装完成后被调用。
它可能会组装额外的渲染数据到顶点数据缓冲区,也可能只是重置一些渲染状态。
由各渲染子节点自己实现,mask的还原缓冲区操作就是通过这个函数调用的。
walk函数到这里结束了。

this.autoMergeBatches(this._currComponent!);
-------------------------------------------------------
进行合批操作【TODO!】
最终的结果是把一个设置好的DrawBatch2D,加入到_batches队列中。

这里是一个canvas的结束。

this.resetRenderStates();
-------------------------------------------------------
重置渲染状态

for (; offset < this._batches.length; ++offset) {
const batch = this._batches.array[offset];
scene.addBatch(batch);
}
这里的scene就是此组件所在节点所属的scene,跳转进去

-------------------------------------------------------
this._batches.push(batch);
所有的批次都结束了,等所有scene遍历完,this._batcher.update()函数也结束了。

接着看 this._root!.frameMove(dt);
-------------------------------------------------------
this._batcher.uploadBuffers();
更新顶点缓冲和索引缓冲【【TODO!】】

-------------------------------------------------------
for (let i = 0; i < scenes.length; i++) {
scenes[i].update(stamp);
}
更新各类灯光
短期内不会深入
-------------------------------------------------------
cameraList.sort((a: Camera, b: Camera) => a.priority - b.priority);
this._pipeline.render(cameraList);
根据权重排序相机
这里的render跳转进去是
this._device.queue.submit(this._commandBuffers);
渲染管线中生成命令缓冲区并压入到this._device.queue中
-------------------------------------------------------
this._device.present();
上屏当前交换链缓冲,整个渲染流程结束

-------------------------------------------------------

写的比较凌乱,代码跳来跳去的,建议大家可以对照源码看一下,总结完还是挺有收获的。

有些看代码过程中总结的一些点图中都写出了,大家可以对照图看。

后面我会着重看一下渲染管线的流程,只不过我也是在摸着石头过河,进度会比较慢。
 

有问题可以给我留言,我会在第一时间回复~

 如果觉得有帮助请给我点赞并收藏哦~您的支持是我最大的鼓励~

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

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

相关文章

华为机试 - 对称美学

目录 题目描述 输入描述 输出描述 用例 题目解析 算法源码 题目描述 对称就是最大的美学&#xff0c;现有一道关于对称字符串的美学。已知&#xff1a; 第1个字符串&#xff1a;R第2个字符串&#xff1a;BR第3个字符串&#xff1a;RBBR第4个字符串&#xff1a;BRRBRBBR…

达标的字符串

1、题目 给定一个数 NNN&#xff0c;想象只由 0 和 1 两种字符组成的所欲长度为 NNN 的字符串。 如果某个字符串&#xff0c;任何 0 字符的左边都有 1 紧挨着&#xff0c;认为这个字符串达标。 返回有多少达标的字符串。 2、思路 前几项推导&#xff1a; 两种解法&#x…

【滤波专题-第6篇】小波阈值去噪方法看这一篇就明白了~(附MATLAB实现)

小波阈值去噪的算法是近些年比较流行的一种滤波方法&#xff0c;由于其阈值函数有着众多的改进方式和改进空间&#xff0c;改进阈值函数也往往可以作为创新点和亮点写到论文中&#xff0c;所以对于正在搞相关研究的同学们写论文是比较友好的&#xff08;轻松水论文方式1&#x…

【高并发】超卖一人一单问题

一、超卖问题 1. 超卖场景 高并发场景下用户下单&#xff0c;存在如下所示的超卖问题&#xff0c;其产生的主要原因是一个线程刚读出库存值&#xff0c;还没进行修改时&#xff0c;另一个线程也读出来该库存值&#xff0c;从而导致这两个线程在进行下单时&#xff0c;对同一个…

【无人机】基于Matlab实现四旋翼无人机几何跟踪控制

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

红星美凯龙寻找「反弹线」

文|螳螂观察 作者| 青月 疫情三年&#xff0c;绝大多数企业都处在「水深火热」之中&#xff0c;到今天&#xff0c;这个情况仍未得到改善。 作为自媒体&#xff0c;「螳螂观察」的感触颇深&#xff0c;疫情之前对一家企业表示肯定的词一般是「暴涨」、「同比大增」&#xff…

C语言竞赛

目录 1059:C语言竞赛 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 思路: 1.结构: 2.判断素数 3.输出宽度调整 代码: 时间复杂度: 总结: 题目链接: 1059:C语言竞赛 C 语言竞赛是浙江大学计算机学院主持的一个欢乐的竞赛。…

vue3+vite使用element-plus

前言&#xff1a; vue3vite的项目中使用 element-plus的教程。 官方地址&#xff1a; 安装 | Element Plusa Vue 3 based component library for designers and developershttps://element-plus.gitee.io/zh-CN/guide/installation.html#%E6%B5%8F%E8%A7%88%E5%99%A8%E7%9B%B…

考研408数据结构 · 开端

引言数据结构在学什么对C语言掌握要求数据结构三要素逻辑结构数据运算存储结构数据结构在学什么 如何把问题信息化 场景&#xff1a;现在需要将1000本不同类型的书摆放到图书馆的书架上。研究问题&#xff1a;采取什么办法摆放&#xff1f;方法有&#xff1a; ① 不管不顾从头…

进程优先级与环境变量

目录 一、进程优先级 1.优先级与权限 2.查看进程优先级 3.PRI与NI 4.修改进程的优先级 5.进程优先级的注意事项 二、进程的其他概念 1.竞争性 2.独立性 3.并行和并发 三、环境变量 1.什么是环境变量 2.环境变量的分类 3.查看环境变量内容 &#xff08;1&#xf…

uniapp easycom教程

easycom 是 uniapp 的一种组件自动引入的规则&#xff0c;使用这种规则可以使满足规则的组件无需注册直接使用。 接下来我们来看一眼效果 这里可以看到我并没有进行组件注册而是直接使用了组件&#xff0c;这样的效果就是通过 easycom 的自定义规则来实现的。 来看一眼我的自…

pandas模块使用介绍

pandas模块使用介绍1.pandas简介 ​ pandas 是基于NumPy 的一种工具&#xff0c;该工具是为解决数据分析任务而创建的。Pandas 纳入了大量库和一些标准的数据模型&#xff0c;提供了高效地操作大型数据集所需的工具。pandas提供了大量能使我们快速便捷地处理数据的函数和方法。…

专访 | 徐鹏程:开源,就是酷

OpenMLDB&#xff1a; 请先来一段自我介绍吧。 徐鹏程&#xff1a; 我本科就读于上海交通大学&#xff0c;硕士在伊利诺伊大学香槟分校&#xff0c;专业都是电子与计算机工程&#xff0c;感兴趣的方向有机器学习在生物信息等领域的应用、计算机系统与架构、分布式系统等。平时…

Java数据结构与Java算法学习Day08---关于树的深度学习(简略笔记记录)

目录 一、平衡树 119 1.1 2-3查找树 119 1.1.1 2-结点和3-结点的含义 119 1.1.2查找 120 1.2 2-3查找树的插入 121 1.2.1 向2-结点中插入新建 121 1.2.2向一棵树只含有一个3-结点的树中插入新建 121 1.2.3向一个父结点为2-结点的3-结点中插入新建 121 1.2.4向一个父…

数据库建表设计技巧

1.名字 建表的时候&#xff0c;给表、字段和索引起个好名字&#xff0c;真的太重要了。 1.1 见名知意 名字就像表、字段和索引的一张脸&#xff0c;可以给人留下第一印象。 好的名字&#xff0c;言简意赅&#xff0c;见名知意&#xff0c;让人心情愉悦&#xff0c;能够提高…

基于go-micro微服务的实战-zipkin实现全链路追踪(九)

基于go-micro微服务的实战-zipkin实现全链路追踪(九) 文章最后附带完整代码 Zipkin是 Twitter 的一个开源项目&#xff0c;基于 Google Dapper实现。可以使用它来收集各个服务器上请求链路的跟踪数据。除了面向开发的API接口之外&#xff0c;它也提供了方便的 UI 组件帮助我们…

代码随想录算法训练营第五十六天| LeetCode583. 两个字符串的删除操作、LeetCode72. 编辑距离

一、LeetCode583. 两个字符串的删除操作 1&#xff1a;题目描述&#xff08;583. 两个字符串的删除操作&#xff09; 给定两个单词 word1 和 word2 &#xff0c;返回使得 word1 和 word2 相同所需的最小步数。 每步 可以删除任意一个字符串中的一个字符。 2&#xff1a;解题思…

细数一下Java中反射机制及反射的优缺点

1. 反射的概念 反射 机制指的是&#xff0c;程序在运行时能够获取自身的信息。在 java 中只要给定类的名字&#xff0c;就能够获取类的所有属性和方法。 反射是 Java 中很多高级特性的基础&#xff0c;比如 注解、动态代理 以及 Spring Ioc、AOP 等技术都需要借助反射来实现。…

再见 if…elif…!使用 Python 装饰器轻松搞定!

大家好&#xff0c;今天在 Github 阅读 EdgeDB[1] 的代码&#xff0c;发现它在处理大量if…elif…else的时候&#xff0c;巧妙地使用了装饰器&#xff0c;方法设计精巧&#xff0c;分享给大家一下&#xff0c;欢迎收藏学习&#xff0c;喜欢点赞支持&#xff0c;技术交流见文末。…