Cocos2dx 3.x 的渲染之DrawCall

news2024/9/25 0:40:13

渲染顺序

Cocos2dx的渲染过程可以类比于油画的绘制,后渲染的总是会盖在先渲染的上层。所以顺序的重要性不言而喻。
根节点是Director 中的 _runningScene,由Scene::Visit()开始 收集整个场景的绘制命令,visit()的代码实现是在基类Node中的,核心代码是:

int i = 0;

if(!_children.empty())
{
	//有子节点时,先对所有的子节点排序,先绘制_localZOrder 小于0的子节点 然后绘制自身,最后绘制其他节点
    sortAllChildren(); 
    // draw children zOrder < 0
    for(auto size = _children.size(); i < size; ++i)
    {
        auto node = _children.at(i);

        if (node && node->_localZOrder < 0)
            node->visit(renderer, _modelViewTransform, flags);
        else
            break;
    }
    // self draw
    if (visibleByCamera)
        this->draw(renderer, _modelViewTransform, flags);

    for(auto it=_children.cbegin()+i, itCend = _children.cend(); it != itCend; ++it)
        (*it)->visit(renderer, _modelViewTransform, flags);
}
else if (visibleByCamera)
{
	//直接绘制 自身
    this->draw(renderer, _modelViewTransform, flags);
}

这里的绘制顺序类似于2叉树的中序遍历。
_localZOrder 的初始值为0,也就是说默认情况下,先绘制父节点,后绘制子节点。深挖一下sortAllChildren()调用的排序逻辑,会发现的排序中还用到一个_orderOfArrival :

std::sort(std::begin(nodes), std::end(nodes), [](_T* n1, _T* n2) {
    return (n1->_localZOrder == n2->_localZOrder && n1->_orderOfArrival < n2->_orderOfArrival) || n1->_localZOrder < n2->_localZOrder;
});

上述逻辑表示子节点的排序是优先按照_localZOrder 进行升序排列,在_localZOrder相同时再按照_orderOfArrival 进行升序排列。

_orderOfArrival 的值是通过调用下边这个函数来赋值,其中s_globalOrderOfArrival是一个全局自增变量。

void Node::updateOrderOfArrival() {
	_orderOfArrival = (++s_globalOrderOfArrival);
}

这个函数主要是在自身添加到父节点上调用的(还有一处是reorderChild())。
做一下总结:

  1. 默认先绘制自身,然后绘制子节点,先添加的子节点先绘制
  2. 可以通过修改_localZOrder 来改变自身的绘制顺序,值越小越先绘制,当值小于0时会比父节点要先绘制即在父节点下层。

绘制命令

cocos先遍历所有的节点,将绘制命令收集在一个 RenderQueue 中。绘制命令的类型有以下几种:

/**Enum the type of render command. */
enum class Type
{
    /** Reserved type.*/
    UNKNOWN_COMMAND,
    /** Quad command, used for draw quad.*/
    QUAD_COMMAND,
    /**Custom command, used for calling callback for rendering.*/
    CUSTOM_COMMAND,
    /**Batch command, used for draw batches in texture atlas.*/
    BATCH_COMMAND,
    /**Group command, which can group command in a tree hierarchy.*/
    GROUP_COMMAND,
    /**Mesh command, used to draw 3D meshes.*/
    MESH_COMMAND,
    /**Primitive command, used to draw primitives such as lines, points and triangles.*/
    PRIMITIVE_COMMAND,
    /**Triangles command, used to draw triangles.*/
    TRIANGLES_COMMAND
};

像常用的Sprite 使用的是TRIANGLES_COMMAND,对应的类是 TrianglesCommand。在对cocos的性能优化中很重要的一条就是DrawCall的优化。减少DrawCall常用且最有效的手段就是合批,即将一些满足一定条件的绘制命令的数据收集起来,然后再调用绘制的接口。还是以TrianglesCommand为例, 它会根据自身的部分数据生成一个_materialID,_materialID相同的TrianglesCommand能够合批渲染

void TrianglesCommand::generateMaterialID()
{
    // glProgramState is hashed because it contains:
    //  *  uniforms/values
    //  *  glProgram
    //
    // we safely can when the same glProgramState is being used then they share those states
    // if they don't have the same glProgramState, they might still have the same
    // uniforms/values and glProgram, but it would be too expensive to check the uniforms.
    struct {
        void* glProgramState;
        GLuint textureId;
        GLenum blendSrc;
        GLenum blendDst;
    } hashMe;

    // NOTE: Initialize hashMe struct to make the value of padding bytes be filled with zero.
    // It's important since XXH32 below will also consider the padding bytes which probably 
    // are set to random values by different compilers.
    memset(&hashMe, 0, sizeof(hashMe)); 

    hashMe.textureId = _textureID;
    hashMe.blendSrc = _blendType.src;
    hashMe.blendDst = _blendType.dst;
    hashMe.glProgramState = _glProgramState;
    _materialID = XXH32((const void*)&hashMe, sizeof(hashMe), 0);
}

cocos2dx 的 Renderer 在处理TrianglesCommand时,会先将连续的TrianglesCommand缓存起来,自动对其中 _materialID 连续相同的命令进行合批 (主要代码位于Renderer::drawBatchedTriangles()函数中)。注意这里的两个粗体的连续,举个例子:


RenderQueue = [ //原始的队列
1	TrianglesCommand(_materialID1,data1),  
2	TrianglesCommand(_materialID2,data2),
3	TrianglesCommand(_materialID1,data3),
4	OtherCommand(data4),
5	TrianglesCommand(_materialID1,data5),
6	TrianglesCommand(_materialID1,data6),
7	TrianglesCommand(_materialID1,data7),
8	TrianglesCommand(_materialID3,data8),
9	OtherCommand(data9),
... ...
]
TrianglesCacheQueue = [] //三角形绘制命令的缓存
TrianglesDrawQueue = [] //三角形实际的绘制命令

开始遍历RenderQueue:
123 是TrianglesCommand 按顺序放入缓存
到4时发现不再是TrianglesCommand,此时先处理 TrianglesCacheQueue =[
1	TrianglesCommand(_materialID1,data1),  
2	TrianglesCommand(_materialID2,data2),
3	TrianglesCommand(_materialID1,data3),
]
遍历发现没有连续相同的_materialID,即不能合批
TrianglesDrawQueue = TrianglesCacheQueue 
逐条执行TrianglesDrawQueue中的绘制命令
执行4 OtherCommand的绘制
继续往后,5678 是TrianglesCommand 按顺序放入缓存
到9时发现不再是TrianglesCommand,此时先处理 TrianglesCacheQueue =[
1	TrianglesCommand(_materialID1,data5),  
2	TrianglesCommand(_materialID1,data6),
3	TrianglesCommand(_materialID1,data7),
4	TrianglesCommand(_materialID3,data8),
]
遍历发现前三条可以合批
TrianglesDrawQueue = [
1  	TrianglesCommand(_materialID1,data5 + data6 + data7),  
2	TrianglesCommand(_materialID3,data8),
]
继续处理后续的命令 ... ...

DrawCall优化

了解上文这些逻辑后,我们知道可以利用自动合批来降低DrawCall,除了在开发中刻意避免打断合批外,使用合图(将同一图层中的精灵图像打包成一个大的图集)是一个常用的技巧。

此外,我们还可以在游戏开发中针对性的进行手动合批:

类背包界面的 DrawCall优化 TODO

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

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

相关文章

k8s StorageClass 存储类

文章目录 一、概述1、StorageClass 对象定义2、StorageClass YAML 示例 二、StorageClass 字段1、provisioner&#xff08;存储制备器&#xff09;1.1、内置制备器1.2、第三方制备器 2、reclaimPolicy&#xff08;回收策略&#xff09;3、allowVolumeExpansion&#xff08;允许…

从碎片到整合:EasyCVR平台如何重塑城市感知系统的视频数据生态

随着城市化进程的加速&#xff0c;城市感知系统作为智慧城市的重要组成部分&#xff0c;正逐步成为提升城市管理效率、保障公共安全、优化资源配置的关键手段。EasyCVR视频汇聚融合平台&#xff0c;凭借其强大的数据整合、智能分析与远程监控能力&#xff0c;在城市感知系统中扮…

Sam Altman的博客:The Intelligence Age

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

thinkphp 做分布式服务+读写分离+分库分表+负载均衡(分区)(后续接着写)

thinkphp 做分布式服务读写分离分库分表负载均衡&#xff08;分区&#xff09; 引言 thinkphp* 大道至简负载均衡分布式服务一、读写分离1、读写分离的实现方式2、主从同步的三种模式2-1、异步模式&#xff08;mysql async-mode&#xff09;2-2、半同步模式&#xff08;mysql s…

红帽rhce含金量?考到能拿多少钱工资?

随着目前国内的it职业飞速发展&#xff0c;rhce已经成为it职业的首选认证、高薪亮点。主要是linux人才出现大比例短缺的状况&#xff0c;很多企业对linux人才的需求也继续升温。 这个时候大家如果抓住了机会&#xff0c;那么实力就能得到质的提升&#xff0c;那么小编针对红帽…

OpenAI GPT-3 API: What is the difference between davinci and text-davinci-003?

题意&#xff1a;OpenAI GPT-3 API&#xff1a;davinci 和 text-davinci-003 有什么区别 问题背景&#xff1a; Im testing the different models for OpenAI, and I noticed that not all of them are developed or trained enough to give a reliable response. 我正在测试…

论文阅读【时间序列】ModerTCN (ICLR2024)

【时间序列】ModerTCN (ICLR2024) 原文链接&#xff1a;ModernTCN: A Modern Pure Convolution Structure for General Time Series Analysis 代码仓库&#xff1a;ModerTCN 简易版本实现代码可以参考&#xff1a;&#xff08;2024 ICLR&#xff09;ModernTCN&#xff1a;A Mod…

谁是AI界的老司机?谁最“纯洁”?谁能通过暧昧小短文的终极考验?

AI的能力已经让人们惊叹不已&#xff0c;不管是帮你写文章、答疑解惑&#xff0c;还是生成艺术作品&#xff0c;几乎无所不能。但如果让AI来解读一篇暗藏玄机、暧昧十足的小短文&#xff0c;结果会怎样&#xff1f;今天&#xff0c;我们就把几款顶流AI大模型拉出来&#xff0c;…

Cobalt Strike的下载与基本用法

CobaltStrike4.8 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;tgf3 what Cobalt Strike是一款渗透测试神器&#xff0c;常被业界人称为CS神器。Cobalt Strike已经不再使用MSF而是作为单独的平台使用&#xff0c;它分为客户端与服务端&#xff0c;服务端是一个&…

C++的扩充和封装

作业&#xff1a; 手动封装一个顺序表&#xff08;SeqList&#xff09;,分文件编译实现 有私有成员&#xff1a;顺序表数组的起始地址 ptr、 顺序表的总长度&#xff1a;size、顺序表的实际长度&#xff1a;len 成员函数&#xff1a;初始化 init(int n) 判空&#xff1a;em…

Vue:默认插槽

目录 一.性质 1.内容分发 2.无名称标识 3.作用域 4.使用方式 二.使用 1.父组件 2.子组件 三.代码 1.父组件代码 2.子组件代码 四.效果 一.性质 1.内容分发 默认插槽允许组件的使用者定义一些内容&#xff0c;这些内容会被插入到组件模板中的特定位置。这有助于实…

C++:类中的特殊关键字,运算重载符

1.My_string类中重载以下的运算符&#xff1a; 、[] 、>、<、、>、<、&#xff01;、、输入输出(>>、<<) 主函数&#xff1a; #include <iostream> #include "my_string.h"using namespace std;int main() {My_string s1("cat…

QT客户端发送HTTP请求此时服务器到底收到了哪些数据?

一个Http请求包括 请求行 请求头 空行 请求体 下面是示例&#xff1a; 1,2,3,4分别代表上面的四个部分&#xff0c;我只是做了一些解析&#xff0c;具体可以结合代码 1. post / HTTP/1.1 2.GET请求头包括Host(主机名),user-agent&#xff08;客户端标识符&#xff09;&am…

AI Agent智能应用从0到1定制开发Langchain+LLM全流程解决方案与落地实战

大模型微调实战&#xff1a;精通、指令微调、开源大模型微调、对齐与垂直领域应用29套AI全栈大模型项目实战&#xff0c;人工智能视频课程-多模态大模型&#xff0c;微调技术训练营&#xff0c;大模型多场景实战&#xff0c;AI图像处理&#xff0c;AI量化投资&#xff0c;OPenC…

fiddler抓包11_列表显示服务器IP (配置文件)

请求列表默认不显示服务器IP字段&#xff0c;也无法从定制列窗口添加&#xff0c;可以修改CustomRules.js实现。 ① 菜单栏“Rules”&#xff08;规则&#xff09; - “Customize Rules...”&#xff08;自定义规则&#xff09;&#xff0c;打开CustomRules.js文件。 &#xf…

HarmonyOS NEXT:解密从概念到实践的技术创新与应用前景

HarmonyOS是目前华为手机所搭载的鸿蒙系统&#xff0c;它在Open Harmony的基础上兼容了安卓的AOSP&#xff0c;所以可以使用安卓APK应用&#xff0c;HarmonyOS属于华为在当前阶段过渡使用的系统&#xff0c;原生鸿蒙的应用生态尚未发展起来&#xff0c;兼容安卓应用可以让用户有…

【AI大模型】通义大模型API接口实现

目录 一、基础环境安装 &#xff08;一&#xff09;OpenAI Python SDK安装 &#xff08;二&#xff09;DashScope SDK安装 二、OPENAI接口实现 &#xff08;一&#xff09;文本输入 &#xff08;二&#xff09;流式输出 &#xff08;三&#xff09;图像输入 &#xff0…

Python 字符串的常见方法

Python 字符串的常见方法 字符串是 Python 中非常重要的数据类型之一。在日常编程中&#xff0c;我们经常需要对字符串进行各种操作&#xff0c;比如分割、连接、替换等。Python 提供了丰富的字符串方法&#xff0c;使得这些操作变得简单而高效。本文将详细介绍一些常见的字符…

【Docker】Docker快速入门

Docker学习笔记 一、Docker概述 为什么会出现Docker? 安卓开发流程&#xff1a;apk(java开发的)发布到应用商店&#xff0c;用户安装apk即可使用。 后端开发流程&#xff1a; jar(java开发的)带上环境发布到Docker仓库&#xff0c;用户从Docker仓库拉取镜像并部署。 总结…

关于Python升级以后脚本不能运行的问题

近日将Python从3.11升级到了3.12&#xff0c;然后把几个包例如numpy等也通过pip给upgrade了一下&#xff0c;结果原来运行的好好的脚本&#xff0c;都运行不了了&#xff0c;还出现各种报错。怀疑是自己升级了环境导致的&#xff0c;因此通过搜索引擎检索了一下&#xff0c;有这…