#1 RenderQueue 不同于2.0时代的按照节点树一个个地去直接绘制,现在会根据一些条件设置每一批次渲染到特定的队列,进行队列渲染,可以更少地改变状态机的状态达到类似流水线的功能也更符合现在GPU的工作原理
class RenderQueue {public: /** RenderCommand will be divided into Queue Groups. */ //对队列根据对应的渲染选项进行分组, 同组的一起渲染 enum QUEUE_GROUP { /**Objects with globalZ smaller than 0.*/ GLOBALZ_NEG = 0, /**Opaque 3D objects with 0 globalZ.*/ OPAQUE_3D = 1, /**Transparent 3D objects with 0 globalZ.*/ TRANSPARENT_3D = 2, /**2D objects with 0 globalZ.*/ GLOBALZ_ZERO = 3, /**Objects with globalZ bigger than 0.*/ GLOBALZ_POS = 4, QUEUE_COUNT = 5, };public: /**Constructor.*/ RenderQueue(); /**Push a renderCommand into current renderqueue.*/ void push_back(RenderCommand* command); /**Return the number of render commands.*/ //返回队列中渲染命令command的数量 ssize_t size() const; /**Sort the render commands.*/ //根据一定规则对command队列进行排序 void sort(); /**Treat sorted commands as an array, access them one by one.*/ //重载下标运算符 RenderCommand* operator[](ssize_t index) const; /**Clear all rendered commands.*/ //清除未渲染的command命令 void clear(); /**Realloc command queues and reserve with given size. Note: this clears any existing commands.*/ //重新根据给定数量生成队列 void realloc(size_t reserveSize); /**Get a sub group of the render queue.*/ //返回相应组的commands inline std::vector& getSubQueue(QUEUE_GROUP group) { return _commands[group]; } /**Get the number of render commands contained in a subqueue.*/ //返回相应组的commands数量 inline ssize_t getSubQueueSize(QUEUE_GROUP group) const { return _commands[group].size();} /**Save the current DepthState, CullState, DepthWriteState render state.*/ //保存当前的深度测试状态, 裁剪状态, 深度缓存写入状态 void saveRenderState(); /**Restore the saved DepthState, CullState, DepthWriteState render state.*///重置当前的深度测试状态, 裁剪状态, 深度缓存写入状态 void restoreRenderState(); protected: /**The commands in the render queue.*/ 根据对应分组类型分好的commands 渲染队列 std::vector _commands[QUEUE_COUNT]; /**Cull state.*/ //裁剪测试是否打开 bool _isCullEnabled; //深度测试是否打开 /**Depth test enable state.*/ bool _isDepthEnabled; //深度缓存是否可写入 /**Depth buffer write state.*/ GLboolean _isDepthWrite;};复制代码
我们看到RenderQueue是按照 enum Type 对renderCommand 进行分组管理的渲染队列, 并提过保存renderstate的方法方便在不同组拥有不同renderstate的情况 ##1.2RenderStackElement 这是个内部使用的结构体构成保存渲染队列和当前正在渲染的队列的Id的堆栈结构
//the struct is not used outside.struct RenderStackElement{ int renderQueueID; ssize_t currentIndex;};复制代码
#2Renderer 重头戏
/* Class responsible for the rendering in.Whenever possible prefer to use `TrianglesCommand` objects since the renderer will automatically batch them. */class CC_DLL Renderer{public: /**The max number of vertices in a vertex buffer object.*/ //顶点缓存对象尺寸的最大值 (单个VBO中最多保存多少顶点属性) static const int VBO_SIZE = 65536; /**The max number of indices in a index buffer.*/ //顶点索引缓存对象尺寸的最大值(这里写死的......毕竟2d都是四边形, 代表4个顶点6个索引绘制2个三角形组成一个4变形) static const int INDEX_VBO_SIZE = VBO_SIZE * 6 / 4; /**The rendercommands which can be batched will be saved into a list, this is the reserved size of this list.*/ //预保留的批次三角形渲染命令的数量 static const int BATCH_TRIAGCOMMAND_RESERVED_SIZE = 64; /**Reserved for material id, which means that the command could not be batched.*/ // 代表不需要绑定到batch_triagCommand 的material_Id static const int MATERIAL_ID_DO_NOT_BATCH = 0; /**Constructor.*/ Renderer(); /**Destructor.*/ ~Renderer(); //TODO: manage GLView inside Render itself //初始化各个平台对应的GLView void initGLView(); /** Adds a `RenderComamnd` into the renderer */ //添加新的Command到Renderer渲染器中 (先加到渲染队列中) void addCommand(RenderCommand* command); /** Adds a `RenderComamnd` into the renderer specifying a particular render queue ID */ //添加新的Command到Renderer渲染器中 (添加到指定的渲染队列中为啥不再写个ID?下面写了我喜欢) void addCommand(RenderCommand* command, int renderQueue); /** Pushes a group into the render queue */ //将一个渲染组添加到指定渲染队列中 void pushGroup(int renderQueueID); /** Pops a group from the render queue */ //将一个渲染组从当前渲染队列提取出来 void popGroup(); /** Creates a render queue and returns its Id */ //创建渲染队列, 返回渲染队列ID int createRenderQueue(); /** Renders into the GLView all the queued `RenderCommand` objects */ //实际渲染的函数, 将所有在Renderqueue中的command命令渲染 void render(); /** Cleans all `RenderCommand`s in the queue */ //清理所有的command void clean(); /** Clear GL buffer and screen */ //清理GL持有的缓存和 屏幕 void clear(); /** set color for clear screen */ //设置清理后显示的颜色 void setClearColor(const Color4F& clearColor); /* returns the number of drawn batches in the last frame */ //返回最后一帧内的批量渲染批数 ssize_t getDrawnBatches() const { return _drawnBatches; } /* RenderCommands (except) TrianglesCommand should update this value */ //添加批量渲染的批数 void addDrawnBatches(ssize_t number) { _drawnBatches += number; }; /* returns the number of drawn triangles in the last frame */ //获取批量渲染中用到的顶点数 ssize_t getDrawnVertices() const { return _drawnVertices; } /* RenderCommands (except) TrianglesCommand should update this value */ //添加对应的顶点数(triangleCommand内的值) void addDrawnVertices(ssize_t number) { _drawnVertices += number; }; /* clear draw stats */ //清理绘制的一些状态将batch批数和绘制的顶点数清零 void clearDrawStats() { _drawnBatches = _drawnVertices = 0; } /** * Enable/Disable depth test * For 3D object depth test is enabled by default and can not be changed * For 2D object depth test is disabled by default */ //开启或关闭深度测试 void setDepthTest(bool enable); //This will not be used outside. //行内展开函数: 返回GroupCommandManager类的对象,后面讲解 inline GroupCommandManager* getGroupCommandManager() const { return _groupCommandManager; }; /** returns whether or not a rectangle is visible or not */ //测试是否在视角和视野内可见, 不可见丢掉不绘制 bool checkVisibility(const Mat4& transform, const Size& size);protected: //Setup VBO or VAO based on OpenGL extensions //设置需要用到的缓存 void setupBuffer(); //设置顶点缓冲对象 和顶点数组对象 void setupVBOAndVAO(); //设置顶点缓冲对象 void setupVBO(); //z暂时不知道 void mapBuffers(); //根据drawnbatches 和 *vertices 绘制批量渲染的命令 void drawBatchedTriangles(); //Draw the previews queued triangles and flush previous context //提交绘制 void flush(); //提交2d绘制 void flush2D(); //提交3d绘制 void flush3D(); //提交三角形绘制(好像最后都走这个函数) void flushTriangles(); //对RenderCommand进行处理 void processRenderCommand(RenderCommand* command); //类似每个node渲染对象的visit函数类似,用于根据情况设置RenderQueue中的command的具体数值 void visitRenderQueue(RenderQueue& queue); //根据给定的TrianglesCommand持有的vertexData 和 indexData 添加到具体的渲染属性中 void fillVerticesAndIndices(const TrianglesCommand* cmd); /* clear color set outside be used in setGLDefaultValues() */ Color4F _clearColor; //保存 各个GroupId的堆栈 std::stack _commandGroupStack; //保存各个renderGroup,RenderQueue的list std::vector_renderGroups; // 这个暂时先不看了就 MeshCommand* _lastBatchedMeshCommand; //这个保存的是已经从RenderQueue中提取出来的需要渲染的三角形图元渲染命令 std::vector _queuedTriangleCommands; //for TrianglesCommand //坐标,颜色,纹理坐标等属性合成的结构体数组 V3F_C4B_T2F _verts[VBO_SIZE]; //绘制4变形的索引数组 GLushort _indices[INDEX_VBO_SIZE]; //保存的当前绑定的VAO GLuint _buffersVAO; //保存的当前绑定的一个顶点属性数组VBO 和一个顶点索引VBO GLuint _buffersVBO[2]; //0: vertex 1: indices // Internal structure that has the information for the batches //一个Batches里使用的内部结构体 struct TriBatchToDraw { TrianglesCommand* cmd; // needed for the Material(意思是相同的材质ID(32位hash值)也就是说相同批次的结构体内的渲染命令是不需要修改状态机状态的,直接根据给定的顶点属性和索引直接绘制就行了) GLushort indicesToDraw; GLushort offset; }; // capacity of the array of TriBatches // 同一批次的渲染上限 int _triBatchesToDrawCapacity; // the TriBatches //嘿丫丫, 一批次渲染的结构体数组 TriBatchToDraw* _triBatchesToDraw; //需要用到的顶点数 int _filledVertex; //需要用到的索引书 int _filledIndex; //这个真不知道干啥的 bool _glViewAssigned; // stats ssize_t _drawnBatches; ssize_t _drawnVertices; //the flag for checking whether renderer is rendering bool _isRendering; bool _isDepthTestFor2D; GroupCommandManager* _groupCommandManager; #if CC_ENABLE_CACHE_TEXTURE_DATA EventListenerCustom* _cacheTextureListener;#endif};复制代码
##2.2GroupCommandManager 在CCGroupCommand文件内
class GroupCommandManager : public Ref{public: int getGroupID(); void releaseGroupID(int groupID);protected: friend class Renderer; GroupCommandManager(); ~GroupCommandManager(); bool init(); std::unordered_map_groupMapping; std::vector _unusedIDs;};int GroupCommandManager::getGroupID(){ //Reuse old id if (!_unusedIDs.empty()) { int groupID = *_unusedIDs.rbegin(); _unusedIDs.pop_back(); _groupMapping[groupID] = true; return groupID; } //Create new ID// int newID = _groupMapping.size(); int newID = Director::getInstance()->getRenderer()->createRenderQueue(); _groupMapping[newID] = true; return newID;}复制代码
看代码这是个管理GroupCommandId 和GroupCommand的类..... 没想到啊没想到 这个GroupCommand竟然是用来设置renderQueue的,跟我想的GroupType没关系......这里的GroupId实际上就是RenderQueueID.....
GroupCommand::GroupCommand(){ _type = RenderCommand::Type::GROUP_COMMAND; _renderQueueID = Director::getInstance()->getRenderer()->getGroupCommandManager()->getGroupID();}void GroupCommand::init(float globalOrder){ _globalOrder = globalOrder; auto manager = Director::getInstance()->getRenderer()->getGroupCommandManager(); manager->releaseGroupID(_renderQueueID); _renderQueueID = manager->getGroupID();}复制代码
#3 Renderer 各函数分析 这里会分析的尽量详细 ##3.1Renderer 构造方法
Renderer::Renderer() :_lastBatchedMeshCommand(nullptr) ,_filledVertex(0) ,_filledIndex(0) ,_glViewAssigned(false) ,_isRendering(false) ,_isDepthTestFor2D(false) ,_triBatchesToDraw(nullptr) ,_triBatchesToDrawCapacity(-1) #if CC_ENABLE_CACHE_TEXTURE_DATA ,_cacheTextureListener(nullptr) #endif { _groupCommandManager = new (std::nothrow) GroupCommandManager(); //新建groupCommandManager 管理renderQueue _commandGroupStack.push(DEFAULT_RENDER_QUEUE); //在ID堆栈中压入默认渲染队列 RenderQueue defaultRenderQueue; _renderGroups.push_back(defaultRenderQueue);//然后在renderqueue对象数组的尾部链上默认渲染队列的对象 _queuedTriangleCommands.reserve(BATCH_TRIAGCOMMAND_RESERVED_SIZE); //设置需要保存的渲染指令的上限 // default clear color // 默认清屏颜色 _clearColor = Color4F::BLACK; // for the batched TriangleCommand // 同一批次最大渲染三角图元数 _triBatchesToDrawCapacity = 500; // 好吧直接就申请内存 我稀饭 _triBatchesToDraw = (TriBatchToDraw*) malloc(sizeof(_triBatchesToDraw[0]) * _triBatchesToDrawCapacity); } 复制代码
##3.2~Render()析构函数 没啥的就是清理该清理的内存 解绑改解绑的glhandler 解绑事件监听
Renderer::~Renderer(){ _renderGroups.clear(); _groupCommandManager->release(); //这里删除直接删俩, 估计创建的时候也是直接创建的俩 glDeleteBuffers(2, _buffersVBO); // 释放TriBatchesToDraw结构体数组 free(_triBatchesToDraw); if (Configuration::getInstance()->supportsShareableVAO()) { //如果支持VAO的话 , 删掉已绑定的 glDeleteVertexArrays(1, &_buffersVAO); GL::bindVAO(0); }#if CC_ENABLE_CACHE_TEXTURE_DATA Director::getInstance()->getEventDispatcher()->removeEventListener(_cacheTextureListener);#endif}复制代码
##3.3 一系列设置glView的方法
void Renderer::initGLView(){#if CC_ENABLE_CACHE_TEXTURE_DATA //可以看到这个是在 这个对象重新被创建的时候(或者重新唤醒的时候)调用 setUpBuffer() _cacheTextureListener = EventListenerCustom::create(EVENT_RENDERER_RECREATED, [this](EventCustom* event){ /** listen the event that renderer was recreated on Android/WP8 */ this->setupBuffer(); }); Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(_cacheTextureListener, -1);#endif setupBuffer(); //手动滑稽,原来这个使用来判断glview是否被分配的 _glViewAssigned = true;}void Renderer::setupBuffer(){ if(Configuration::getInstance()->supportsShareableVAO()) { setupVBOAndVAO(); } else { setupVBO(); }}void Renderer::setupVBOAndVAO(){ //generate vbo and vao for trianglesCommand //生成顶点属性数组对象 glGenVertexArrays(1, &_buffersVAO); //绑定(GL里很多都是这样生成绑定, 绑定使用, 状态机) GL::bindVAO(_buffersVAO); //生成两个VBO ,分配Handler 给_bufferVBO[0] 和_bufferVBO[1] glGenBuffers(2, &_buffersVBO[0]); //设置顶点属性结构体的数组 至顶点属性缓存 glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(_verts[0]) * VBO_SIZE, _verts, GL_DYNAMIC_DRAW); // vertices // 坐标 glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_POSITION); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(V3F_C4B_T2F), (GLvoid*) offsetof( V3F_C4B_T2F, vertices)); // colors // 颜色 glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_COLOR); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(V3F_C4B_T2F), (GLvoid*) offsetof( V3F_C4B_T2F, colors)); // tex coords // 纹理坐标 glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_TEX_COORD); glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, sizeof(V3F_C4B_T2F), (GLvoid*) offsetof( V3F_C4B_T2F, texCoords)); //设置顶点索引数组 至顶点索引缓存 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * INDEX_VBO_SIZE, _indices, GL_STATIC_DRAW); // Must unbind the VAO before changing the element buffer. //在使用前先解除相应的绑定, 使用时再绑定 GL::bindVAO(0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); CHECK_GL_ERROR_DEBUG();}//不支持VAO的话 就只需要使用VBO了void Renderer::setupVBO(){ glGenBuffers(2, &_buffersVBO[0]); // Issue #15652 // Should not initialzie VBO with a large size (VBO_SIZE=65536), // it may cause low FPS on some Android devices like LG G4 & Nexus 5X. // It's probably because some implementations of OpenGLES driver will // copy the whole memory of VBO which initialzied at the first time // once glBufferData/glBufferSubData is invoked. // For more discussion, please refer to https://github.com/cocos2d/cocos2d-x/issues/15652// mapBuffers();}// 绑定属性值至缓存void Renderer::mapBuffers(){ // Avoid changing the element buffer for whatever VAO might be bound. GL::bindVAO(0); glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(_verts[0]) * VBO_SIZE, _verts, GL_DYNAMIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * INDEX_VBO_SIZE, _indices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); CHECK_GL_ERROR_DEBUG();}复制代码
##3.4 addCommand 添加渲染命令到指定队列 另外一个不加renderQueueID的实际上是获取堆栈最上方的ID再调用以下方法
void Renderer::addCommand(RenderCommand* command, int renderQueue){ CCASSERT(!_isRendering, "Cannot add command while rendering"); CCASSERT(renderQueue >=0, "Invalid render queue"); CCASSERT(command->getType() != RenderCommand::Type::UNKNOWN_COMMAND, "Invalid Command Type"); _renderGroups[renderQueue].push_back(command);}复制代码
##3.5 RenderQueue相关的一系列方法
//压渲染队列入栈void Renderer::pushGroup(int renderQueueID){ CCASSERT(!_isRendering, "Cannot change render queue while rendering"); _commandGroupStack.push(renderQueueID);}//渲染队列出栈void Renderer::popGroup(){ CCASSERT(!_isRendering, "Cannot change render queue while rendering"); _commandGroupStack.pop();}//创建一个新的渲染队列int Renderer::createRenderQueue(){ RenderQueue newRenderQueue; _renderGroups.push_back(newRenderQueue); return (int)_renderGroups.size() - 1; //纯朴的c++风格}复制代码
##3.5 visitRenderQueue IDE是个好东西还可以查看调用结构,直接就能找到调用顺序 这个是对RenderQueue进行访问并设置渲染属性的方法,长长的一大段函数
void Renderer::visitRenderQueue(RenderQueue& queue){ //调用RenderQueue的saveRenderState()函数 /* void RenderQueue::saveRenderState() { _isDepthEnabled = glIsEnabled(GL_DEPTH_TEST) != GL_FALSE; _isCullEnabled = glIsEnabled(GL_CULL_FACE) != GL_FALSE; glGetBooleanv(GL_DEPTH_WRITEMASK, &_isDepthWrite); CHECK_GL_ERROR_DEBUG(); } */ //看到是根据全局的参数确定了一系列的渲染标识位 queue.saveRenderState(); // //Process Global-Z < 0 Objects // // 处理全局深度小于0 的commands对象 const auto& zNegQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::GLOBALZ_NEG); if (zNegQueue.size() > 0) { if(_isDepthTestFor2D) { glEnable(GL_DEPTH_TEST); glDepthMask(true); glEnable(GL_BLEND); //嘿呀又看到熟悉的_defaultState了 RenderState::StateBlock::_defaultState->setDepthTest(true); RenderState::StateBlock::_defaultState->setDepthWrite(true); RenderState::StateBlock::_defaultState->setBlend(true); } else { glDisable(GL_DEPTH_TEST); glDepthMask(false); glEnable(GL_BLEND); RenderState::StateBlock::_defaultState->setDepthTest(false); RenderState::StateBlock::_defaultState->setDepthWrite(false); RenderState::StateBlock::_defaultState->setBlend(true); } glDisable(GL_CULL_FACE); RenderState::StateBlock::_defaultState->setCullFace(false); for (auto it = zNegQueue.cbegin(); it != zNegQueue.cend(); ++it) { //这里暂时先知道这里是用来处理每个command属性的方法 processRenderCommand(*it); } //提交渲染批次 -- 先画一批 flush(); } // //Process Opaque Object // // 处理不透明的command对象 const auto& opaqueQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::OPAQUE_3D); if (opaqueQueue.size() > 0) { //Clear depth to achieve layered rendering glEnable(GL_DEPTH_TEST); glDepthMask(true); glDisable(GL_BLEND); glEnable(GL_CULL_FACE); RenderState::StateBlock::_defaultState->setDepthTest(true); RenderState::StateBlock::_defaultState->setDepthWrite(true); RenderState::StateBlock::_defaultState->setBlend(false); RenderState::StateBlock::_defaultState->setCullFace(true); for (auto it = opaqueQueue.cbegin(); it != opaqueQueue.cend(); ++it) { processRenderCommand(*it); } flush(); } // //Process 3D Transparent object // //处理3D 的透明对象(要开混合, 深度测试, 裁剪) const auto& transQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::TRANSPARENT_3D); if (transQueue.size() > 0) { glEnable(GL_DEPTH_TEST); glDepthMask(false); glEnable(GL_BLEND); glEnable(GL_CULL_FACE); RenderState::StateBlock::_defaultState->setDepthTest(true); RenderState::StateBlock::_defaultState->setDepthWrite(false); RenderState::StateBlock::_defaultState->setBlend(true); RenderState::StateBlock::_defaultState->setCullFace(true); for (auto it = transQueue.cbegin(); it != transQueue.cend(); ++it) { processRenderCommand(*it); } flush(); } // //Process Global-Z = 0 Queue // // 深度为0的对象(大部分的2d渲染对象都在这个范围内) const auto& zZeroQueue = queue.getSubQueue(RenderQueue::QUEUE_GROUP::GLOBALZ_ZERO); if (zZeroQueue.size() > 0) { if(_isDepthTestFor2D) { glEnable(GL_DEPTH_TEST); glDepthMask(true); glEnable(GL_BLEND); RenderState::StateBlock::_defaultState->setDepthTest(true); RenderState::StateBlock::_defaultState->setDepthWrite(true); RenderState::StateBlock::_defaultState->setBlend(true); } else { glDisable(GL_DEPTH_TEST); glDepthMask(false); glEnable(GL_BLEND); RenderState::StateBlock::_defaultState->setDepthTest(false); RenderState::StateBlock::_defaultState->setDepthWrite(false); RenderState::StateBlock::_defaultState->setBlend(true); } glDisable(GL_CULL_FACE); RenderState::StateBlock::_defaultState->setCullFace(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) { if(_isDepthTestFor2D) { glEnable(GL_DEPTH_TEST); glDepthMask(true); glEnable(GL_BLEND); RenderState::StateBlock::_defaultState->setDepthTest(true); RenderState::StateBlock::_defaultState->setDepthWrite(true); RenderState::StateBlock::_defaultState->setBlend(true); } else { glDisable(GL_DEPTH_TEST); glDepthMask(false); glEnable(GL_BLEND); RenderState::StateBlock::_defaultState->setDepthTest(false); RenderState::StateBlock::_defaultState->setDepthWrite(false); RenderState::StateBlock::_defaultState->setBlend(true); } glDisable(GL_CULL_FACE); RenderState::StateBlock::_defaultState->setCullFace(false); for (auto it = zPosQueue.cbegin(); it != zPosQueue.cend(); ++it) { processRenderCommand(*it); } flush(); } // 重置RenderState 为下一个队列做准备 //还是贴一下方法帮助回忆一下 /* void RenderQueue::restoreRenderState(){ if (_isCullEnabled) { glEnable(GL_CULL_FACE); RenderState::StateBlock::_defaultState->setCullFace(true); } else { glDisable(GL_CULL_FACE); RenderState::StateBlock::_defaultState->setCullFace(false); } if (_isDepthEnabled) { glEnable(GL_DEPTH_TEST); RenderState::StateBlock::_defaultState->setDepthTest(true); } else { glDisable(GL_DEPTH_TEST); RenderState::StateBlock::_defaultState->setDepthTest(false); } glDepthMask(_isDepthWrite); RenderState::StateBlock::_defaultState->setDepthWrite(_isDepthEnabled); CHECK_GL_ERROR_DEBUG();} */ queue.restoreRenderState();}复制代码
##3.6processRenderCommand 处理各个RenderCommand
void Renderer::processRenderCommand(RenderCommand* command){ //获取当前command 的type auto commandType = command->getType(); // 3角图元渲染 if( RenderCommand::Type::TRIANGLES_COMMAND == commandType) { //有种好像flush是不是调用了GLFinish()的感觉,这个应该很耗性能的 // flush other queues flush3D(); //强转 auto cmd = static_cast(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); //根据command的顶点索引数增量 _filledIndex += cmd->getIndexCount(); //根据command的顶点属性书增量 _filledVertex += cmd->getVertexCount(); } //如果是网格渲染 else if (RenderCommand::Type::MESH_COMMAND == commandType) { //先提交掉2d的东西 flush2D(); //强转 auto cmd = static_cast (command); //如果设置了不进行批次渲染或最后一个批次渲染的命令为空或者command的MaterialID跟批次的不符就不加到批次中,进行单独渲染 if (cmd->isSkipBatching() || _lastBatchedMeshCommand == nullptr || _lastBatchedMeshCommand->getMaterialID() != cmd->getMaterialID()) { flush3D(); CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_MESH_COMMAND"); if(cmd->isSkipBatching()) { // XXX: execute() will call bind() and unbind() // but unbind() shouldn't be call if the next command is a MESH_COMMAND with Material. // Once most of cocos2d-x moves to Pass/StateBlock, only bind() should be used. //mesh的先不看估计看注释,里面的大部分实现转到Pass跟StateBlock两个类了,只有bind()还在 cmd->execute(); } else { cmd->preBatchDraw(); //预处理批次 cmd->batchDraw(); //批次渲染(估计也没有画,只是加到队列里) _lastBatchedMeshCommand = cmd; //将其设为批次的最后一个meshCommand } } else { CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_MESH_COMMAND"); cmd->batchDraw(); //直接批量绘制(等等看看这到底是个啥) } } else if(RenderCommand::Type::GROUP_COMMAND == commandType) { flush(); int renderQueueID = ((GroupCommand*) command)->getRenderQueueID(); CCGL_DEBUG_PUSH_GROUP_MARKER("RENDERER_GROUP_COMMAND"); visitRenderQueue(_renderGroups[renderQueueID]); CCGL_DEBUG_POP_GROUP_MARKER(); } else if(RenderCommand::Type::CUSTOM_COMMAND == commandType) { flush(); auto cmd = static_cast (command); CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_CUSTOM_COMMAND"); //估计这个execute要自己实现,毕竟叫custom cmd->execute(); } else if(RenderCommand::Type::BATCH_COMMAND == commandType) { flush(); auto cmd = static_cast (command); CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_BATCH_COMMAND"); cmd->execute(); } else if(RenderCommand::Type::PRIMITIVE_COMMAND == commandType) { flush(); auto cmd = static_cast (command); CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_PRIMITIVE_COMMAND"); cmd->execute(); } else { CCLOGERROR("Unknown commands in renderQueue"); }}复制代码
具体看来这个execute才是重头啊,等会再看毕竟不在这个文件里,先看其他的 ##3.7flush系列函数
//渲染2d和3dvoid Renderer::flush(){ flush2D(); flush3D();}//2d的话实际就是画四边形,四边形实际就是三角形void Renderer::flush2D(){ flushTriangles();}// 3d渲染void Renderer::flush3D(){ if (_lastBatchedMeshCommand) { CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_BATCH_MESH"); _lastBatchedMeshCommand->postBatchDraw(); _lastBatchedMeshCommand = nullptr; }}//提交绘制三角形void Renderer::flushTriangles(){ drawBatchedTriangles();}复制代码
##3.8 drawBatchedTriangles 绘制三角形
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; //默认材质ID为-1 int prevMaterialID = -1; //是否是第一个命令(决定了你是不是一个批次的No.1) bool firstCommand = true; for(auto it = std::begin(_queuedTriangleCommands); it != std::end(_queuedTriangleCommands); ++it) { const auto& cmd = *it; //获取cmd的材质Id auto currentMaterialID = cmd->getMaterialID(); //cmd是否要加入批次渲染 const bool batchable = !cmd->isSkipBatching(); //根据cmd刷新顶点属性值, 索引值, 顶点数, 索引数 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++; //经过对上面代码的分析发现 如果queuedTriangles里面的 的cmd 如果没有按照材质ID排序的话, 这个批量刷新的存在其实还不如不存在 //下面就是把数据复制到GL的缓存中 /************** 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); // 使用glMapBuffer 然cpu端能直接获取缓存区的索引直接进行修改 // 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); //获取到指针后直接对缓存的数据进行修改(修改的大小为perSizeOfverts*_filledVertex, 数据为_verts) memcpy(buf, _verts, sizeof(_verts[0]) * _filledVertex); glUnmapBuffer(GL_ARRAY_BUFFER); glBindBuffer(GL_ARRAY_BUFFER, 0); //索引缓存就不这么麻烦了,数据量相较于verts数组来说少的可怜 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * _filledIndex, _indices, GL_STATIC_DRAW); } //else 后面就是比较平常的内存显存交互的方式了 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; iuseMaterial(); 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;}复制代码
发现需要更多的分析RenderQueue的函数了比如sort啊啥的 妈的如果不做那个什么排序的话完全没有意义啊, 难怪每次debug下都提示capacity扩充
##3.9 fillVerticesAndIndices 根据cmd扩充要用到的_verts 和_indices数据 刷新_filledVertex 和_filledIndices 的值
void Renderer::fillVerticesAndIndices(const TrianglesCommand* cmd){ //本来还想说怎么没看到_verts和_indices数据怎么刷新的,好吧眼瞎 //这种方式很好的避免了内存的申请很销毁的开销(同时意味着可能在一些小的游戏里你要为这些空闲的一直得不到使用的内存负责, 可以修改VBOSize的大小来适应不同的游戏) memcpy(&_verts[_filledVertex], cmd->getVertices(), sizeof(V3F_C4B_T2F) * cmd->getVertexCount()); // fill vertex, and convert them to world coordinates //获取视图模型矩阵 const Mat4& modelView = cmd->getModelView(); for(ssize_t i=0; i < cmd->getVertexCount(); ++i) { //额先转到相机空间再绘制吗(好吧这个不是可以在顶点着色器做吗,难道cocos的所有坐标转换除了mvp外都是在cpu端做的?) modelView.transformPoint(&(_verts[i + _filledVertex].vertices)); } // fill index const unsigned short* indices = cmd->getIndices(); for(ssize_t i=0; i< cmd->getIndexCount(); ++i) { _indices[_filledIndex + i] = _filledVertex + indices[i]; } _filledVertex += cmd->getVertexCount(); _filledIndex += cmd->getIndexCount();}复制代码
##4.0 RenderQueue:sort
//仅对Zoder进行排序static bool compareRenderCommand(RenderCommand* a, RenderCommand* b){ return a->getGlobalOrder() < b->getGlobalOrder();}//对深度进行排序static bool compare3DCommand(RenderCommand* a, RenderCommand* b){ return a->getDepth() > b->getDepth();}void RenderQueue::sort(){ // Don't sort _queue0, it already comes sorted std::sort(std::begin(_commands[QUEUE_GROUP::TRANSPARENT_3D]), std::end(_commands[QUEUE_GROUP::TRANSPARENT_3D]), compare3DCommand); std::sort(std::begin(_commands[QUEUE_GROUP::GLOBALZ_NEG]), std::end(_commands[QUEUE_GROUP::GLOBALZ_NEG]), compareRenderCommand); std::sort(std::begin(_commands[QUEUE_GROUP::GLOBALZ_POS]), std::end(_commands[QUEUE_GROUP::GLOBALZ_POS]), compareRenderCommand);}复制代码