您的位置:首页 > 移动开发 > Cocos引擎

cocos2dx Sprite使用同一张纹理会只调用一次渲染

2017-02-22 01:12 447 查看
cocos2dx3.x中,Sprite调用draw函数时会添加一个TrianglesCommand类型的渲染指令。

CCSprite.cpp

_trianglesCommand.init(_globalZOrder,
_texture,
getGLProgramState(),
_blendFunc,
_polyInfo.triangles,
transform,
flags);

renderer->addCommand(&_trianglesCommand);


在render执行这个指令时,TrianglesCommand并不会立即执行opengl的渲染,而是把它添加到一个std::vector

CCRender.cpp

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();
}


这里做了一个限制,如果当前顶点缓冲区满了,就会立即执行opengl的渲染,所以当Sprite足够多时,虽然他们使用的是同一张纹理,也会调用多次渲染。

CCRender.cpp

void Renderer::drawBatchedTriangles()
{
if(_queuedTriangleCommands.empty())
return;

CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_BATCH_TRIANGLES");

_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 *************/
auto conf = Configuration::getInstance();
if (conf->supportsShareableVAO() && conf->supportsMapBuffer())
{
//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
{
// 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;
}


这个函数就是用来绘制TriangleCommand指令的,这里出现了一个新的类型TriBatchToDraw

struct TriBatchToDraw {
TrianglesCommand* cmd;  // needed for the Material
GLushort indicesToDraw;
GLushort offset;
};


TriBatchToDraw 有三个成员变量,渲染指令cmd,需要渲染的顶点数量indicesToDraw,顶点数据在顶点缓冲区初始的偏移位置offset

如果一个渲染指令的MaterialID和上一个指令的MaterialID相等,就会把两条渲染指令整合到一起,如果两条指令使用了相同的MaterialID但是两条指令不连续,就不会整合到一个渲染指令里。

所以cocos2dx的Sprite使用同一张纹理时,必须是连续添加到父节点,中间不会有使用其他纹理的Sprite添加到父节点,才会达到SpriteBatchNode的效果
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  cocos2d-x
相关文章推荐