cocos2dx的2.x版本和cocos2dx的3.x版本在渲染流程上有了很大的不同浓恳,通過(guò)某度大家可以查到寂恬。
其中還有一個(gè)優(yōu)化就是3.x使用了auto_batching技術(shù)來(lái)替代了2.x時(shí)代中的spriteBatchNode,通過(guò)了解我們能知道广辰,想要通過(guò)auto_batching來(lái)降低drawcall的話,有三個(gè)條件:
1.需確保精靈對(duì)象擁有相同的TextureId(精靈表單spritesheet);
2.確保它們都使用相同的混合函數(shù)
3.沒(méi)有使用shader去進(jìn)行修改
但為什么會(huì)有這三個(gè)條件吶翩活?或者說(shuō)都滿足這幾個(gè)條件后,那是因?yàn)槭裁磳?dǎo)致這些精靈可以auto_batching吶便贵?
首先菠镇,咱們先來(lái)看下3.x的渲染流程:(以下都是coocs2dx 3.7.1版本的源碼,沒(méi)用的我就刪掉了)
1.drawScene開(kāi)始繪制場(chǎng)景
if (_runningScene)
? ? {
? ? ? ? //clear draw stats
? ? ? ? _renderer->clearDrawStats();
? ? ? ? //render the scene
? ? ? ? _runningScene->render(_renderer);
? ? ? ? _eventDispatcher->dispatchEvent(_eventAfterVisit);
? ? }
2.遍歷場(chǎng)景的子節(jié)點(diǎn)
第一步中 的_runningScene->render(_renderer) render函數(shù)中會(huì)有一步visit(renderer, transform, 0)操作承璃,去遍歷所有子節(jié)點(diǎn)
3.調(diào)用每一個(gè)子節(jié)點(diǎn)的draw函數(shù)
voidSprite::draw(Renderer*renderer,constkmMat4&transform,booltransformUpdated)
{
????????// Don't do calculate the culling if the transform was not updated
????????_insideBounds=transformUpdated?isInsideBounds():_insideBounds;
? ? ? ? //在這個(gè)渲染命令上會(huì)有差別利耍,我用的3.7.1版本的渲染命令已經(jīng)換成了下面的_trianglesCommand,之前的是_quadCommand
具體是哪個(gè)版本換的盔粹,我沒(méi)有去跟蹤隘梨。
????????if(_insideBounds)
????????{
????????????????_quadCommand.init(_globalZOrder, _texture->getName(), _shaderProgram, _blendFunc,&_quad,1, transform);
????????????????renderer->addCommand(&_quadCommand);
????????}
????????if(_insideBounds)
????? ? {
? ? ????????? ? _trianglesCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, _polyInfo.triangles, transform, flags);
????????? ? ? ? renderer->addCommand(&_trianglesCommand);
????? ? }
}
4.初始化TrianglesCommand對(duì)象,這就是渲染命令
上面的代碼就是重點(diǎn)了舷嗡,初始化_trianglesCommand對(duì)象轴猎,這就是TrianglesCommand,渲染命令进萄。
其實(shí)渲染命令不僅僅只有TrianglesCommand捻脖,還有其他的锐峭,比如CustomCommand,自定義渲染命令郎仆,顧名思義只祠,就是我們用戶自己定制的命令,由于我沒(méi)有使用過(guò)扰肌,就不介紹了抛寝。
然后,接著就調(diào)用addCommand函數(shù)將渲染命令加入隊(duì)列曙旭。
這里有一點(diǎn)盗舰,也很重要,由于渲染命令有好幾種桂躏,所以addCommand的時(shí)候钻趋,其實(shí)是會(huì)根據(jù)不同的命令類型把渲染命令添加到不同的隊(duì)列。本文只想針對(duì)TrianglesCommand剂习,所以就忽略這一點(diǎn)蛮位,假設(shè)我們的所有命令都是TrianglesCommand。
5.draw函數(shù)執(zhí)行完鳞绕,就輪到渲染邏輯干事了
6.開(kāi)始渲染
Render的processRenderCommand會(huì)根據(jù)不同的Command來(lái)進(jìn)行篩選失仁,并做對(duì)應(yīng)的處理
//以下是對(duì)TRIANGLES_COMMAND 命令的處理
if( RenderCommand::Type::TRIANGLES_COMMAND == commandType)
? ? {
? ? ? ? // flush other queues
? ? ? ? flush3D();
? ? ? ? auto cmd = static_cast<TrianglesCommand*>(command);
? ? ? ? // flush own queue when buffer is full
? ? ? ? if(_filledVertex + cmd->getVertexCount() > VBO_SIZE || _filledIndex + cmd->getIndexCount() > INDEX_VBO_SIZE)
? ? ? ? {
? ? ? ? ? ? CCASSERT(cmd->getVertexCount()>= 0 && cmd->getVertexCount() < VBO_SIZE, "VBO for vertex is not big enough, please break the data down or use customized render command");
? ? ? ? ? ? CCASSERT(cmd->getIndexCount()>= 0 && cmd->getIndexCount() < INDEX_VBO_SIZE, "VBO for index is not big enough, please break the data down or use customized render command");
? ? ? ? ? ? drawBatchedTriangles();
? ? ? ? }
? ? ? ? // queue it
? ? ? ? _queuedTriangleCommands.push_back(cmd);
? ? ? ? _filledIndex += cmd->getIndexCount();
? ? ? ? _filledVertex += cmd->getVertexCount();
? ? }
在這個(gè)函數(shù)drawBatchedTriangles()中,你會(huì)發(fā)現(xiàn):(想自己了解細(xì)節(jié)的話们何,可以到CCRenderer.cpp中查看)
for(const auto& cmd : _queuedTriangleCommands)
{
? ? ? ? auto currentMaterialID = cmd->getMaterialID();
? ? ? ? const bool batchable = !cmd->isSkipBatching();
? ? ? ? fillVerticesAndIndices(cmd);
? ? ? ? // in the same batch ?
? ? ? ? if (batchable && (prevMaterialID == currentMaterialID || firstCommand))
? ? ? ? {
? ? ? ? ? ? _triBatchesToDraw[batchesTotal].indicesToDraw += cmd->getIndexCount();
? ? ? ? ? ? _triBatchesToDraw[batchesTotal].cmd = cmd;
? ? ? ? }else{
????????}
}
//上面的粗體部分就是判斷是否是同一紋理萄焦。
你肯定會(huì)驚訝,what冤竹?拂封??這就行了鹦蠕?為什么要用auto_batching必須要相同紋理冒签、相同混合函數(shù)、相同shader吶片部?
關(guān)鍵是這個(gè):(sprite的draw函數(shù))
_trianglesCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, _polyInfo.triangles, transform, flags);
跟這 TrianglesCommand::init()進(jìn)去看下镣衡,你會(huì)發(fā)現(xiàn):
if( _textureID != textureID || _blendType.src != blendType.src || _blendType.dst != blendType.dst || _glProgramState != glProgramState) {
? ? ? ? _textureID = textureID;
? ? ? ? _blendType = blendType;
? ? ? ? _glProgramState = glProgramState;
? ? ? ? generateMaterialID();
? ? }
在這個(gè)加粗的函數(shù)中,你會(huì)發(fā)現(xiàn)档悠,它做了什么吶廊鸥?
// 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);
他會(huì)給自身的command命令綁定一個(gè)_materialID 。這就是auto_batching的原因辖所。