Cocos中,对于使用相同贴图且没有自定义uniform变量且采用默认绘制指令TrianglesCommand或QuaCommand的节点可以进行自动合批绘制
合批绘制的本质是将两个模型动态合并为一个模型,并使用相同的材质在一个DrawCall中进行绘制,以减少DrawCall数量,提高运行效率。
Cocos判断两个uniform是否能够合批,靠的是通过GLProgram、TextureID和BlendState联合计算的一个Hash值,这个Hash值是渲染指令对象中的一个叫_materialID的成员。材质ID的生成函数如下:
void QuadCommand::generateMaterialID()
{
_skipBatching = false;
if(_glProgramState->getUniformCount() == 0)
{
int glProgram = (int)_glProgramState->getGLProgram()->getProgram();
int intArray[4] = { glProgram, (int)_textureID, (int)_blendType.src, (int)_blendType.dst};
_materialID = XXH32((const void*)intArray, sizeof(intArray), 0);
}
else
{
_materialID = Renderer::MATERIAL_ID_DO_NOT_BATCH;
_skipBatching = true;
}
}
合批相关逻辑:在遍历渲染指令进行glDrawElements渲染的的时候,不是每个指令都会调用一次glDrawElements渲染(也就是一次drawcall),遍历的时候判断当前指令的材质ID(_materialID)是否跟上次的材质ID相同,如果相同则不会进行drawcall而是继续遍历,直到_lastMaterialID != newMaterialID,才会执行上一个渲染指令的drawcall。遍历结束后,会执行剩余的最后一批drawcall。代码里注释: //Draw any remaining triangles
在渲染器类Render的成员函数 drawBatchedTriangles()、drawBatchedQuads()中实现:
void Renderer::drawBatchedTriangles()
{
//TODO: we can improve the draw performance by insert material switching command before hand.
int indexToDraw = 0;
int startIndex = 0;
//Upload buffer to VBO
if(_filledVertex <= 0 || _filledIndex <= 0 || _batchedCommands.empty())
{
return;
}
if (Configuration::getInstance()->supportsShareableVAO())
{
//Bind VAO
GL::bindVAO(_buffersVAO);
//Set VBO data
glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(_verts[0]) * _filledVertex, nullptr, GL_DYNAMIC_DRAW);
void *buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
memcpy(buf, _verts, sizeof(_verts[0])* _filledVertex);
glUnmapBuffer(GL_ARRAY_BUFFER);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * _filledIndex, _indices, GL_STATIC_DRAW);
}
else
{
#define kQuadSize sizeof(_verts[0])
glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(_verts[0]) * _filledVertex , _verts, GL_DYNAMIC_DRAW);
GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * _filledIndex, _indices, GL_STATIC_DRAW);
}
//Start drawing vertices in batch,合批逻辑在此
for(const auto& cmd : _batchedCommands)
{
auto newMaterialID = cmd->getMaterialID();
if(_lastMaterialID != newMaterialID || newMaterialID == MATERIAL_ID_DO_NOT_BATCH)
{
//Draw quads
if(indexToDraw > 0)
{
glDrawElements(GL_TRIANGLES, (GLsizei) indexToDraw, GL_UNSIGNED_SHORT, (GLvoid*) (startIndex*sizeof(_indices[0])) );
_drawnBatches++;
_drawnVertices += indexToDraw;
startIndex += indexToDraw;
indexToDraw = 0;
}
//Use new material
cmd->useMaterial();
_lastMaterialID = newMaterialID;
}
indexToDraw += cmd->getIndexCount();
}
//Draw any remaining triangles
if(indexToDraw > 0)
{
glDrawElements(GL_TRIANGLES, (GLsizei) indexToDraw, GL_UNSIGNED_SHORT, (GLvoid*) (startIndex*sizeof(_indices[0])) );
_drawnBatches++;
_drawnVertices += indexToDraw;
}
if (Configuration::getInstance()->supportsShareableVAO())
{
//Unbind VAO
GL::bindVAO(0);
}
else
{
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
_batchedCommands.clear();
_filledVertex = 0;
_filledIndex = 0;
}
版权声明:本文为u012861978原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。