3.0之前cocos2dx渲染机制上存在着一些弊端:不易扩展,不易针对绘制进行优化。
不易扩展:3.0之前每个元素的绘制逻辑全在元素元素内部的draw()方法里。绘制顺序紧密的依赖于UI树。导致无法在多层级之间调整绘制顺序。
不易针对绘制进行优化:各个绘制都分布在每个元素内部,不利于针对绘制进行优化。
3.0新的绘制系统将绘制部分从UI树的遍历中分离出来了。使得绘制系统更灵活,更易于扩展。
新的绘制系统是怎么将绘制部分分离出来的?
3.0版本中多了一个RanderCommand类。另外draw接口也变了
virtual void draw(Renderer *renderer, const Mat4& transform, uint32_t flags);
Node类的draw是空实现,再看看sprite类里的draw方法。
void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
// 检查是否在边界内
_insideBounds = (flags & FLAGS_TRANSFORM_DIRTY) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;
if(_insideBounds)
{
_quadCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, &_quad, 1, transform);
renderer->addCommand(&_quadCommand);
#if CC_SPRITE_DEBUG_DRAW
_customDebugDrawCommand.init(_globalZOrder);
_customDebugDrawCommand.func = CC_CALLBACK_0(Sprite::drawDebugData, this);
renderer->addCommand(&_customDebugDrawCommand);
#endif //CC_SPRITE_DEBUG_DRAW
}
}
draw里初始化了一条QuadCommand,他继承于RanderCommand。之后再将这条命令加入到Render中。并没有执行绘制相关操作。
在看到Director类的drawScene()方法
// draw the scene
if (_runningScene)
{
_runningScene->visit(_renderer, Mat4::IDENTITY, false);
_eventDispatcher->dispatchEvent(_eventAfterVisit);
}
// draw the notifications node
if (_notificationNode)
{
_notificationNode->visit(_renderer, Mat4::IDENTITY, false);
}
if (_displayStats)
{
showStats();
}
_renderer->render();
_eventDispatcher->dispatchEvent(_eventAfterDraw);
当调用完节点的visit (visit里面会调用节点的draw方法)后,就调用了Render的render方法。到这里就可以看出,其实draw里的绘制操作,统一的都以命令的形式统一的加入到render里面,有render来统一处理。于是绘制部分的处理,就从UI树的遍历里分离了出来,这样如果想针对绘制进行优化,那么直接操作几个绘制类就行了。例如cocos2dx会对QuadCommand执行自动批绘制。
在看到Render的render方法里具体做了什么。
void Renderer::render()
{
//Uncomment this once everything is rendered by new renderer
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//TODO setup camera or MVP
_isRendering = true;
if (_glViewAssigned)
{
// cleanup
_drawnBatches = _drawnVertices = 0;
//Process render commands
//1. Sort render commands based on ID
for (auto &renderqueue : _renderGroups)
{
renderqueue.sort();
}
visitRenderQueue(_renderGroups[0]);
flush();
}
clean();
_isRendering = false;
}
首先对命令进行了排序,之后才是对节点进行绘制工作。