姑且从Application::run()开始吧。代码1:
int Application::run()
{
PVRFrameEnableControlWindow(false);//设置注册表PVRFrame隐藏
// Main message loop:
LARGE_INTEGER nLast;
LARGE_INTEGER nNow;
QueryPerformanceCounter(&nLast); //获取当前的计数值,即频率x当前时间
initGLContextAttrs();//设置 context 属性,目前只能设置6个属性 red,green,blue,alpha,depth(深度),stencil(模板),详见GLViewImpl::initWithRect
// Initialize instance and cocos2d.
if (!applicationDidFinishLaunching())
{
return 1;
}
auto director = Director::getInstance();
auto glview = director->getOpenGLView();
// Retain glview to avoid glview being released in the while loop
glview->retain();
while(!glview->windowShouldClose())
{
QueryPerformanceCounter(&nNow);// 取得当前的计数值,即频率x当前时间
if (nNow.QuadPart - nLast.QuadPart > _animationInterval.QuadPart) // 每隔_animationInterval.QuadPart时间绘制一次
{
nLast.QuadPart = nNow.QuadPart - (nNow.QuadPart % _animationInterval.QuadPart);
director->mainLoop();
glview->pollEvents();
}
else
{
Sleep(1);
}
}
// Director should still do a cleanup if the window was closed manually.
if (glview->isOpenGLReady()) // 判断glView窗口是否创建成功
{
director->end(); // 将结束标志设为真,再做一次循环
director->mainLoop(); // 此交循环中将会对资源进行清理,释放
director = nullptr;
}
glview->release();
return 0;
}
渲染之前,先整理一下Director对象,因为Director做了opengl渲染的一些准备工作。创建代码如下,代码2:
Director* Director::getInstance()
{
if (!s_SharedDirector)
{
s_SharedDirector = new (std::nothrow) DisplayLinkDirector(); // new(std::nothrow)表示当创建失败时不抛出异常,直接返回NULL
CCASSERT(s_SharedDirector, "FATAL: Not enough memory");
s_SharedDirector->init();
}
return s_SharedDirector;
}
init函数,代码3:
bool Director::init(void)
{
setDefaultValues();
...
//init TextureCache
initTextureCache(); // 构造_textureCache对象
initMatrixStack(); // 初始化_modelViewMatrixStack,_projectionMatrixStack,_textureMatrixStack三个矩阵
_renderer = new (std::nothrow) Renderer; // 构造render对象
return true;
}
setDefaultValues函数,代码4:
void Director::setDefaultValues(void)
{
Configuration *conf = Configuration::getInstance();
// default FPS
double fps = conf->getValue("cocos2d.x.fps", Value(kDefaultFPS)).asDouble();
_oldAnimationInterval = _animationInterval = 1.0 / fps;
// 是滞显示 FPS
_displayStats = conf->getValue("cocos2d.x.display_fps", Value(false)).asBool();
// GL projection,2d 和 3D,在设置视景体时有区别,默认是3D
std::string projection = conf->getValue("cocos2d.x.gl.projection", Value("3d")).asString();
if (projection == "3d")
_projection = Projection::_3D;
else if (projection == "2d")
_projection = Projection::_2D;
else if (projection == "custom")
_projection = Projection::CUSTOM;
else
CCASSERT(false, "Invalid projection value");
<span style="white-space:pre"> </span>/*RGBA8888,RGBA4444,RGB5A1:为3种像素格式,详见百度。*/
// Default pixel format for PNG images with alpha
std::string pixel_format = conf->getValue("cocos2d.x.texture.pixel_format_for_png", Value("rgba8888")).asString();
if (pixel_format == "rgba8888")
Texture2D::setDefaultAlphaPixelFormat(Texture2D::PixelFormat::RGBA8888);
else if(pixel_format == "rgba4444")
Texture2D::setDefaultAlphaPixelFormat(Texture2D::PixelFormat::RGBA4444);
else if(pixel_format == "rgba5551")
Texture2D::setDefaultAlphaPixelFormat(Texture2D::PixelFormat::RGB5A1);
// PVR v2 has alpha premultiplied ?
bool pvr_alpha_premultipled = conf->getValue("cocos2d.x.texture.pvrv2_has_alpha_premultiplied", Value(false)).asBool();
Image::setPVRImagesHavePremultipliedAlpha(pvr_alpha_premultipled);
}
setDefaultValues设置了FPS,是否显示FPS,投影类型,像素格式等参数。所有的参数都是通过Configuration对象获取的,除此之外,Configuration配置的参数还包括:所支持的MaxTextureSize, MaxModelviewStackDepth, MaxTextureUnits;NPOT(2的幂); PVRTC,ETC,S3TC压缩标准, BGRA8888纹理格式; glDiscardFramebufferEXT; VAO; MaxSupportDirLight, GLExtension.。Configuration的实现这里不展开了。
下面来整理一下具体的渲染过程,进入director->mainLoop(),代码如下,代码5:
void DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop) //只有一种情况会调用到这里来,就是导演类调用end函数
{
_purgeDirectorInNextLoop = false;
purgeDirector(); // 停止场景的所有动作,移除所有监听事件,释放相关的资源
}
else if (_restartDirectorInNextLoop)
{
_restartDirectorInNextLoop = false;
restartDirector(); // 先销毁,再重新创建所有对象
}
else if (! _invalid) // 能否进行下一次渲染,如果开始动画(startAnimation),则渲染
{
drawScene();
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
mainLoop函数通过purge,restart几个标志位控制着渲染的暂停,结束,具体的渲染工作在drawScene中完成。
drawScene函数代码如下,代码6:
void Director::drawScene()
{
// calculate "global" dt
calculateDeltaTime();//计算间隔时间
if (_openGLView)
{
_openGLView->pollEvents();<span style="white-space:pre"> </span>// 调用到glfwPollEvents()分发事件
}
//tick before glClear: issue #533
if (! _paused)
{
_scheduler->update(_deltaTime);
_eventDispatcher->dispatchEvent(_eventAfterUpdate);
}
_renderer->clear();//清除buffer和depth_buffer
/* to avoid flickr, nextScene MUST be here: after tick and before draw.
* FIXME: Which bug is this one. It seems that it can't be reproduced with v0.9
*/
if (_nextScene)<span style="white-space:pre"> </span>// 如果场景有更换的话,更换场景
{
setNextScene();// 清理当前场景_runningScene,将_nextScene设置为_runningScene
}
pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);//将模型矩阵、投影矩阵、纹理矩阵重置,注:此三个矩阵栈里均有两个Identity矩阵,一个用于计算,一个作为母本
if (_runningScene)
{
#if CC_USE_PHYSICS
auto physicsWorld = _runningScene->getPhysicsWorld();
if (physicsWorld && physicsWorld->isAutoStep())
{
physicsWorld->update(_deltaTime, false);
}
#endif
//clear draw stats
_renderer->clearDrawStats();<span style="white-space:pre"> </span>//_drawnBatches = _drawnVertices = 0;
//render the scene
_runningScene->render(_renderer);//绘制运行的场景,包括组装command和执行command,注:每执行完一次,render都会清理一遍command
_eventDispatcher->dispatchEvent(_eventAfterVisit);
}
// draw the notifications node
if (_notificationNode)// 注:此节点不会随场景更换而移除
{
_notificationNode->visit(_renderer, Mat4::IDENTITY, 0);//如果有悬浮节点,则组装command到_renderer命令队列中
}
if (_displayStats)// 是否显示状态信息,即FPS等信息
{
showStats();
}
_renderer->render();//执行命令队列
_eventDispatcher->dispatchEvent(_eventAfterDraw);
popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
_totalFrames++;
// swap buffers
if (_openGLView)
{
_openGLView->swapBuffers();
}
if (_displayStats)
{
calculateMPF();
}
}
drawscene中,要渲染的内容分两部分,一部分是_runningScene,另一部分是_notificationNode(如果显示的话),_notificationNode只是一些测试数据,这里不展开。主要看_runningScene->render(_renderer),代码如下,代码7:
void Scene::render(Renderer* renderer)
{
auto director = Director::getInstance();
Camera* defaultCamera = nullptr;
const auto& transform = getNodeToParentTransform(); // 获取从子节点坐标转换到父节点坐标的矩阵
if (_cameraOrderDirty)
{
stable_sort(_cameras.begin(), _cameras.end(), camera_cmp);
_cameraOrderDirty = false;
}
for (const auto& camera : _cameras) // 注:当_cameras在onEnter的时候会被绑定相应的scene,并加入到scene的_cameras列表中
{
if (!camera->isVisible())
continue;
Camera::_visitingCamera = camera;
if (Camera::_visitingCamera->getCameraFlag() == CameraFlag::DEFAULT)
{
defaultCamera = Camera::_visitingCamera;
}
director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION, Camera::_visitingCamera->getViewProjectionMatrix());
//visit the scene
visit(renderer, transform, 0); // 遍历场景和场景子节点并组装对应的渲染命令,并添加到render的命令列表
renderer->render(); // 遍历渲染列表,并依次执行命令,实现渲染
director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION);
}
Camera::_visitingCamera = nullptr;
}
在render函数里,就可以看出一点opengl的影子了,最起码 设置矩阵->渲染代码->重置矩阵 这个过程大家应该很熟悉。下面再接着往里扒,进入visit函数,代码8:
void Node::visit(Renderer* renderer, const Mat4 &parentTransform, uint32_t parentFlags)
{
// quick return if not visible. children won't be drawn.
if (!_visible)
{
return;
}
uint32_t flags = processParentFlags(parentTransform, parentFlags);
// IMPORTANT:
// To ease the migration to v3.0, we still support the Mat4 stack,
// but it is deprecated and your code should not rely on it
_director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
_director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);
bool visibleByCamera = isVisitableByVisitingCamera();
int i = 0;
if(!_children.empty())
{
sortAllChildren();
// draw children zOrder < 0
for( ; i < _children.size(); i++ )
{
auto node = _children.at(i);
if (node && node->_localZOrder < 0)
node->visit(renderer, _modelViewTransform, flags);
else
break;
}
// self draw, node的draw什么也没做,但它是一个虚函数,实现在他的子类里面,完成的绘制命令的组装,添加添加到renderer的命令列表中(一个node对应0到多个命令对象,具体的命令定义在node的子类中)
if (visibleByCamera)
this->draw(renderer, _modelViewTransform, flags);
for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
(*it)->visit(renderer, _modelViewTransform, flags);
}
else if (visibleByCamera)
{
this->draw(renderer, _modelViewTransform, flags);
}
_director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
// FIX ME: Why need to set _orderOfArrival to 0??
// Please refer to https://github.com/cocos2d/cocos2d-x/pull/6920
// reset for next frame
// _orderOfArrival = 0;
}
visit函数递归调用了自己,重点在draw函数中,node的draw函数什么也没做,但它是一个虚函数,其子类是有实现的,以sprite为例,代码9:
void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
#if CC_USE_CULLING
// Don't do calculate the culling if the transform was not updated
_insideBounds = (flags & FLAGS_TRANSFORM_DIRTY) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;
if(_insideBounds)
#endif
{
_quadCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, &_quad, 1, transform, flags);// 构造渲染命令对象
renderer->addCommand(&_quadCommand);
#if CC_SPRITE_DEBUG_DRAW
_debugDrawNode->clear();
Vec2 vertices[4] = {
Vec2( _quad.bl.vertices.x, _quad.bl.vertices.y ),
Vec2( _quad.br.vertices.x, _quad.br.vertices.y ),
Vec2( _quad.tr.vertices.x, _quad.tr.vertices.y ),
Vec2( _quad.tl.vertices.x, _quad.tl.vertices.y ),
};
_debugDrawNode->drawPoly(vertices, 4, true, Color4F(1.0, 1.0, 1.0, 1.0));
#endif //CC_SPRITE_DEBUG_DRAW
}
}
sprite的draw函数主要就是init了一个_quadCommand命令对象,并添加到了renderer的命令列表中,可见,node->draw就际上就是一个构造rendercommand命令的过程,还没有任何实质的渲染行为。具体的命令对象的结构是什么这里不扒了,可以去看opengl vao,vbo,3.6版本里有6种渲染命令:TRIANGLES_COMMAND,QUAD_COMMAND,MESH_COMMAND,GROUP_COMMAND,CUSTOM_COMMAND,BATCH_COMMAND,PRIMITIVE_COMMAND,这些命令稍后再提。
下面回到代码7,visit函数遍历各node组装好渲染命令,并添加到renderer的命令列表中后,开始调用renderer->render();执行命令,代码如下,代码10:
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)
{
//Process render commands
//1. Sort render commands based on ID
for (auto &renderqueue : _renderGroups)
{
renderqueue.sort();
}
visitRenderQueue(_renderGroups[0]);
}
clean();
_isRendering = false;
}
void Renderer::visitRenderQueue(RenderQueue& queue)
{
queue.saveRenderState();<span style="white-space:pre"> </span>// 获取GL_DEPTH_TEST,GL_CULL_FACE和GL_DEPTH_WRITEMASK开关标志,为了绘制完后决定是否需要关闭这些开关,见restoreRenderState
//
//Process Global-Z < 0 Objects,需要做混合
//
const auto& zNegQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::GLOBALZ_NEG);
if (zNegQueue.size() > 0)
{
if(_isDepthTestFor2D)
{
glEnable(GL_DEPTH_TEST);
glDepthMask(true);
}
else
{
glDisable(GL_DEPTH_TEST);
glDepthMask(false);
}
for (auto it = zNegQueue.cbegin(); it != zNegQueue.cend(); ++it)
{
processRenderCommand(*it);
}
flush();
}
//
//Process Opaque Object
//
const auto& opaqueQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::OPAQUE_3D);
if (opaqueQueue.size() > 0)
{
//Clear depth to achieve layered rendering
glDepthMask(true);
glEnable(GL_DEPTH_TEST);
for (auto it = opaqueQueue.cbegin(); it != opaqueQueue.cend(); ++it)
{
processRenderCommand(*it);
}
flush();
}
//
//Process 3D Transparent object
//
const auto& transQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::TRANSPARENT_3D);
if (transQueue.size() > 0)
{
glEnable(GL_DEPTH_TEST);
glDepthMask(false);
for (auto it = transQueue.cbegin(); it != transQueue.cend(); ++it)
{
processRenderCommand(*it);
}
flush();
}
//
//Process Global-Z = 0 Queue
//
const auto& zZeroQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::GLOBALZ_ZERO);
if (zZeroQueue.size() > 0)
{
if(_isDepthTestFor2D)
{
glEnable(GL_DEPTH_TEST);
glDepthMask(true);
}
else
{
glDisable(GL_DEPTH_TEST);
glDepthMask(false);
}
for (auto it = zZeroQueue.cbegin(); it != zZeroQueue.cend(); ++it)
{
processRenderCommand(*it);
}
flush();
}
//
//Process Global-Z > 0 Queue
//
const auto& zPosQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::GLOBALZ_POS);
if (zPosQueue.size() > 0)
{
for (auto it = zPosQueue.cbegin(); it != zPosQueue.cend(); ++it)
{
processRenderCommand(*it);
}
flush();
}
queue.restoreRenderState();
}
void Renderer::processRenderCommand(RenderCommand* command)
{
auto commandType = command->getType();
if( RenderCommand::Type::TRIANGLES_COMMAND == commandType)
{
//Draw if we have batched other commands which are not triangle command
flush3D();
flushQuads();
//Process triangle command
auto cmd = static_cast<TrianglesCommand*>(command);
//Draw batched Triangles if necessary
if(cmd->isSkipBatching() || _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");
//Draw batched Triangles if VBO is full
drawBatchedTriangles();
}
//Batch Triangles
_batchedCommands.push_back(cmd);
fillVerticesAndIndices(cmd);
if(cmd->isSkipBatching())
{
drawBatchedTriangles();
}
}
else if ( RenderCommand::Type::QUAD_COMMAND == commandType )
{
//Draw if we have batched other commands which are not quad command
flush3D();
flushTriangles();
//Process quad command
auto cmd = static_cast<QuadCommand*>(command);
//Draw batched quads if necessary
if(cmd->isSkipBatching()|| (_numberQuads + cmd->getQuadCount()) * 4 > VBO_SIZE )
{
CCASSERT(cmd->getQuadCount()>= 0 && cmd->getQuadCount() * 4 < VBO_SIZE, "VBO for vertex is not big enough, please break the data down or use customized render command");
//Draw batched quads if VBO is full
drawBatchedQuads();
}
//Batch Quads
_batchQuadCommands.push_back(cmd);
fillQuads(cmd);
if(cmd->isSkipBatching())
{
drawBatchedQuads();
}
}
else if (RenderCommand::Type::MESH_COMMAND == commandType)
{
flush2D();
auto cmd = static_cast<MeshCommand*>(command);
if (cmd->isSkipBatching() || _lastBatchedMeshCommand == nullptr || _lastBatchedMeshCommand->getMaterialID() != cmd->getMaterialID())
{
flush3D();
if(cmd->isSkipBatching())
{
cmd->execute();
}
else
{
cmd->preBatchDraw();
cmd->batchDraw();
_lastBatchedMeshCommand = cmd;
}
}
else
{
cmd->batchDraw();
}
}
else if(RenderCommand::Type::GROUP_COMMAND == commandType)
{
flush();
int renderQueueID = ((GroupCommand*) command)->getRenderQueueID();
visitRenderQueue(_renderGroups[renderQueueID]);
}
else if(RenderCommand::Type::CUSTOM_COMMAND == commandType)
{
flush();
auto cmd = static_cast<CustomCommand*>(command);
cmd->execute();
}
else if(RenderCommand::Type::BATCH_COMMAND == commandType)
{
flush();
auto cmd = static_cast<BatchCommand*>(command);
cmd->execute();
}
else if(RenderCommand::Type::PRIMITIVE_COMMAND == commandType)
{
flush();
auto cmd = static_cast<PrimitiveCommand*>(command);
cmd->execute();
}
else
{
CCLOGERROR("Unknown commands in renderQueue");
}
}
再这里,一次渲染过程结束。
最后,我们现再来看看6个renderercommand类型(细节还没有深入研究,等研究了再整理吧。。)
TRIANGLES_COMMAND(详见TrianglesCommand类):
用于渲染三角形,renderer类中专门有drawBatchedTriangles来批量处理该命令。
QUAD_COMMAND(详见QuadCommand类):
用于渲染4边形,与TRIANGLES_COMMAND类似,renderer类中专门也有drawBatchedQuads来批量处理该命令。
MESH_COMMAND(详见MeshCommand类)。
GROUP_COMMAND(详见GroupCommand类)。
CUSTOM_COMMAND(详见CustomCommand类),可以绑定一个回调函数如:
_customCommand.init(_globalZOrder);
_customCommand.func = CC_CALLBACK_0(DrawTest::onDraw, this, transform,flags);
BATCH_COMMAND(详见BatchCommand类)。
PRIMITIVE_COMMAND(详见BatchCommand类)。