Cocos2d-x与OpenGL底层的感想

前端之家收集整理的这篇文章主要介绍了Cocos2d-x与OpenGL底层的感想前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

1.为什么会卡顿

这篇文章想写一些工作经常碰到的一些问题,为什么我做一个2D游戏,渲染100多个精灵就会卡。

他们同样是做2D游戏,为什么渲染那么多东西帧数非常高,一点卡顿的样子都没有?

这里我们排除一些逻辑因素,在相同游戏逻辑复杂度下。我每帧也没什么逻辑运算也还是卡,这里我们首先排除掉cpu对于游戏帧数瓶颈的限制。我们来谈下,为什么都是2D游戏,我渲染那么少的东西就会卡。其他游戏2D大作渲染那么多东西还是很流畅!

2.OpenGL问题

OpenGL是个非常老旧的渲染API,API层非常单薄,僵硬,程序员能控制的东西非常少,无非就是资源的生成,控制一些渲染缓存的状态,调用Draw接口。其实在现在硬件变革好几代之后API还是那样子,已经跟不上硬件的速度。而且手机游戏中为了兼容性问题,一般游戏都是用OpenGLES2.0这个版本,造成对于硬件性能的浪费。

打个比方如果一台手机可以本来的渲染性能可以渲染基准性能是100分,但是图像接口的问题和驱动层优化问题只能发挥到70分,渲染引擎设计问题继续砍半只能发挥40分,如果程序员对于引擎不够了解只能发挥到30。这样就造成貌似机器性能在飞速发展,但是有些游戏的画面提升不是很快。

当我们无法改变图形接口和驱动的时候,我们能否把可以控制的70分给压榨出来,就是考验一个游戏厂商的实力(大厂商跟硬件公司合作非常密切,而且单一平台上对于优化更好,所以PS4和XBox的游戏画面一直比单机好点。很多硬件厂商会为了某个游戏还会去优化显卡驱动,他们示可以拿到80分甚至90分的性能,小公司是没有那个待遇的)。

其他设计硬件和渲染管线问题以后提。这里简单说明单纯的渲染API的调用,这个也是最好理解的。OpenGL是状态机设计模式,每次调用一个API都会去改变渲染管线中某个功能开关。我们每次调用一个API,就会生成一个指令。这个指令告诉显卡怎么去做,这边是有性能损失的。(API调用OpenGL渲染是异步的过程,在缓冲区中会维护一个渲染命令队列,在某个时机去发送给驱动层怎么做,驱动做的事情以后再说。这个时机可以我们手动去触发glFlush强制发送,但是我们一般没有需求不推荐。我们这里可以理解调用OpenglAPI成同步的。)所以尽量的少调用OpenGL API渲染性能就上去了。说了也是白说,我要画那么多东西,当然要多调接口了,不调接口我怎么画。

这边就引出一个问题,我怎么少调用API,但是可以多画东西呢?批次渲染是最通用也是最简答的做法!

3.批次渲染概念

如果我们把一次渲染比作一群人出游,在一个停车场中。大家准备排队上车。每辆顺序出口出去。但是停车场有个奇怪的规定:相邻穿相同颜色衣服的人可以坐同一辆车出去,如果后面一个如果穿不同颜色衣服人,那么就得新配一辆车。假如100个人穿100种颜色的衣服,那么就得100辆车,如果100穿相同颜色的衣服话只要一辆车。如果排队顺序是A.B.C.D AB如果都穿红色站一起一辆车,C绿一辆车,D红色一辆车。这样就三辆车。如果调换下ABCD顺序变成A.B.D.C,这样就变成两辆车。车越少,那么全部离开停车场越短,更快到目的地旅行了。批次渲染就是劲量让穿相同颜色人,塞进同一部车中。更快的出去

在一般的游戏引擎中,我们都会有一个渲染的队列,每个渲染单元抽样成一个渲染命令。每个命令都会去调用OpenGL API。

如果两个渲染命令一起的画,我们是否可以把两个命令合成一个命令呢?因为OpenGL是状态机啊,第一个命令状态设置后,第二个命令无需重新去设置渲染状态。这样不就少调用API 但是画一样的多东西!这个就是批次渲染的概念。

我们今天只谈下2D游戏,在2D游戏每个渲染命令涉及到OpenGL API其实比较少了,设置Shader绑定TextureDrawElements差不多就这三样,

一般精灵的Shader是一样的,这个我们是可以合并的。这个比较简单。

Texture纹理,每个精灵都有自己的纹理,没办法合并啊!其实有办法,把两张小图拼成一张大图,设置顶点数据的UV坐标!搞定这个也可以合并。

DrawElement 这里我们渲染图元基本都是三角形,如果把三角形的顶点数据放在一个VBO中。那么也是可以合并。

4.coco2d-x中的批次渲染

cocos2d 最早以前有个类型叫BatchNode,这个类型地下绑定精灵如果是同一张纹理的就会生成一个批次渲染,但是这个不是很好用,如果美术改了资源的,有个子节点不是用大图里面纹理会出问题。后面3.X版本,cocos2d-x推出一个技术叫(Auto-batching)、在深度优先的子节点遍历情况,相邻节点如果材质相同就自动合并批次。所以贴图相同的精灵我们是可以合并批次的。

5.实际测试

第一个基准测试,我们上面都不渲染,只是单纯控制Cocos2d-x不到满帧,等下好对比。

const int kDrawNodeNum = 6000;

void TestScene::DrawEmptyNode()
{
    for (int nIndex = 0; nIndex < kDrawNodeNum; ++nIndex){
        int nIndexColor = 0; nIndexColor < 3; ++nIndexColor){        
            Node* pNode = Node::create();
            this->addChild(pNode);
        }
    }
}

第二个我们是使用三张分开图的测试。

void TestScene::DrawPartSprite() { Size size = Director::getInstance()->getVisibleSize(); char* spritename[] = { "green.png",red.pngblue.png" }; 3; ++nIndexColor){ Sprite* pIcon = Sprite::create(spritename[nIndexColor]); pIcon->setAnchorPoint(Vec2::ZERO); pIcon->setPosition(Vec2(200 + (nIndex * 20 + nIndexColor*10 ) % int(size.width),128)">20 + nIndexColor) % int(size.height))); this->addChild(pIcon); } } }
第三个我们使用一张合并图,其实里面东西都一样的。

void TestScene::DrawTogetherSprite() { SpriteFrameCache::getInstance()->addSpriteFramesWithFile(color.plist"); Size size = Director::getInstance()->getVisibleSize(); " }; 3; ++nIndexColor){ Sprite* pIcon = Sprite::createWithSpriteFrameName(spritename[nIndexColor]); pIcon->setAnchorPoint(Vec2::ZERO); pIcon->setPosition(Vec2(10) % 资源就这样,非常简单

非常简单的三个图片,分开渲染是三张分开的图片,合并渲染是用一个合图。

其实代码基本相同。为什么性能竟然有那么大的差距。如果除去cpu性能就是基本性能的消耗。在极端的情况下,我们基本可以达到三倍的提升,三倍其实是个很夸张的数据了。

如果游戏中,如果游戏中精灵批次命中稍微高点,即使对于性能提高10%到20% 对于性能的优化也是有好处的,而你所需要做的可能就是选择好图片去合并成一张大图。

猜你在找的Cocos2d-x相关文章