cocos2dx在处理TRIANGLES_COMMAND会进行优化处理,这个优化是指合并相同材质的指令,并且共用顶点缓存和索引缓存。在无法继续合并或者达到合并上限是会调用drawBatchedTriangles一次性输出合并的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();
}
共享顶点缓存和索引缓存,并且相同材质可以合并到一个渲染批次里,是否相同材质是通过一个哈希值_materialID是否相等判断的。
int glProgram = (int)_glProgram->getProgram();
int intArray[4] = { glProgram, (int)_textureID, (int)_blendType.src, (int)_blendType.dst};
_materialID = XXH32((const void*)intArray, sizeof(intArray), 0);
void Renderer::drawBatchedTriangles()
{
/*没有渲染指令,直接退出*/
if(_queuedTriangleCommands.empty())
return;
_filledVertex = 0;
_filledIndex = 0;
/************** 1: Setup up vertices/indices *************/
/*数组中每个成员是一个渲染批次*/
_triBatchesToDraw[0].offset = 0;
_triBatchesToDraw[0].indicesToDraw = 0;
_triBatchesToDraw[0].cmd = nullptr;
int batchesTotal = 0;
int prevMaterialID = -1;
bool firstCommand = true;
/*遍历所有渲染指令,进行合并处理*/
for(auto it = std::begin(_queuedTriangleCommands); it != std::end(_queuedTriangleCommands); ++it)
{
const auto& cmd = *it;
auto currentMaterialID = cmd->getMaterialID();
const bool batchable = !cmd->isSkipBatching();
/*合并顶点缓存和索引缓存*/
fillVerticesAndIndices(cmd);
// in the same batch ?
if (batchable && (prevMaterialID == currentMaterialID || firstCommand))
{
/*合并到同一个批次进行渲染*/
CC_ASSERT(firstCommand || _triBatchesToDraw[batchesTotal].cmd->getMaterialID() == cmd->getMaterialID() && "argh... error in logic");
_triBatchesToDraw[batchesTotal].indicesToDraw += cmd->getIndexCount();
_triBatchesToDraw[batchesTotal].cmd = cmd;
}
else
{
/*无法合并,增加新的批次*/
// is this the first one?
if (!firstCommand) {
batchesTotal++;
_triBatchesToDraw[batchesTotal].offset = _triBatchesToDraw[batchesTotal-1].offset + _triBatchesToDraw[batchesTotal-1].indicesToDraw;
}
_triBatchesToDraw[batchesTotal].cmd = cmd;
_triBatchesToDraw[batchesTotal].indicesToDraw = (int) cmd->getIndexCount();
// is this a single batch ? Prevent creating a batch group then
if (!batchable)
currentMaterialID = -1;
}
// capacity full ?
if (batchesTotal + 1 >= _triBatchesToDrawCapacity) {
_triBatchesToDrawCapacity *= 1.4;
_triBatchesToDraw = (TriBatchToDraw*) realloc(_triBatchesToDraw, sizeof(_triBatchesToDraw[0]) * _triBatchesToDrawCapacity);
}
prevMaterialID = currentMaterialID;
firstCommand = false;
}
batchesTotal++;
/************** 2: Copy vertices/indices to GL objects *************/
/*opengl es2.0不支持vao,3.0支持vao,这里根据是否支持vao分两种情况处理*/
if (Configuration::getInstance()->supportsShareableVAO()) { /*支持vao,修改vao里顶点数据和索引数据*/ //Bind VAO GL::bindVAO(_buffersVAO); //Set VBO data glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]); // option 1: subdata // glBufferSubData(GL_ARRAY_BUFFER, sizeof(_quads[0])*start, sizeof(_quads[0]) * n , &_quads[start] ); // option 2: data // glBufferData(GL_ARRAY_BUFFER, sizeof(_verts[0]) * _filledVertex, _verts, GL_STATIC_DRAW); // option 3: orphaning + glMapBuffer // FIXME: in order to work as fast as possible, it must "and the exact same size and usage hints it had before." // source: https://www.opengl.org/wiki/Buffer_Object_Streaming#Explicit_multiple_buffering // so most probably we won't have any benefit of using it glBufferData(GL_ARRAY_BUFFER, sizeof(_verts[0]) * _filledVertex, nullptr, GL_STATIC_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 { /*不支持vao,直接修改顶点缓存对象和索引缓存对象的数据*/ // Client Side Arrays #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); // vertices glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, vertices)); // colors glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, colors)); // tex coords glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, texCoords)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * _filledIndex, _indices, GL_STATIC_DRAW); } /*渲染输出,循环的每一次都是一个渲染批次*/ /************** 3: Draw *************/ for (int i=0; i<batchesTotal; ++i) { CC_ASSERT(_triBatchesToDraw[i].cmd && "Invalid batch"); _triBatchesToDraw[i].cmd->useMaterial();
glDrawElements(GL_TRIANGLES, (GLsizei) _triBatchesToDraw[i].indicesToDraw, GL_UNSIGNED_SHORT, (GLvoid*) (_triBatchesToDraw[i].offset*sizeof(_indices[0])) );
_drawnBatches++;
_drawnVertices += _triBatchesToDraw[i].indicesToDraw;
}
/*渲染结束,清理数据*/
/************** 4: Cleanup *************/
if (Configuration::getInstance()->supportsShareableVAO()) { //Unbind VAO GL::bindVAO(0); } else { glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } _queuedTriangleCommands.clear(); _filledVertex = 0; _filledIndex = 0; }