cocos合批绘制

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版权协议,转载请附上原文出处链接和本声明。