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;
}
首先對命令進行了排序,之後纔是對節點進行繪製工作。