Cocos 工作机制

1、cocos内存管理方式

  cocos 是通过引用计数方式管理内存的,主要通过两种方式实现。

  1)手动管理内存

    当我们创建一个实例时,基类Ref里面有一个叫referenceCount的参数会置1,它代表我们引用的次数,使用retain()函数可以使reference+1,release()函数会使referenceCount -1;当这个参数减至0时会被引擎delete掉释放内存。

    

 

 

  2)自动回收池

    有手动就会有自动。

    我们在create()一个对象时,调用new函数创建对象并且还会利用autorelease()方法 把该对象的指针加入自动回收池,

    主线程 每一帧在Application::getInstance()->run()的过程中会调用mainloop()方法

     

 

    在mainloop()方法中会利用单例的PoolManager类 getCurPool()获取当前的自动回收池,并调用自动回收池中的clear()方法,

    

 

     clear()方法里,会遍历移除所有与该自动回收池相关联的对象,并调用一次release()方法,如果这个对象没被调用且referenceCount被减至0就会delete掉。

    若这个对象已经addChild到其他控件上,被引用的话,referenceCount不会被减到0,等到其父节点被清理的时候再回收。

     好处:

    1)无需手动和release()和retain(),避免内存泄露

    2)在每一帧结束后对无用的对象进行自动处理内存回收,方便可靠。

    可以优化的地方

    1)PoolManager管理的是一个_releasePoolStack(存储自动回收池的栈),一般情况下,每一帧结束后,我们只是把当前的池子清空,然后执行下一帧的操作。

    但是我们也应该考虑到释放池本身维护着一个将要执行释放操作的对象列表,如果在一帧内生成了大量的autoRelease对象,将会导致释放池性能下降。

    因此,在生成autorelease对象密集的区域(通常是循环中)的前后,我们最好可以手动创建并释放一个回收池。

    2)autorelease()只有在自动释放池被释放时才会进行一次释放操作,若果对象释放的次数超过了应有的次数,则这个错误在调用autorelease()时并不会被发现,只有当自动释放池被释放时(通知也就是游戏的每一帧结束时),游戏才会崩溃。

    在这种情况下,定位错误就变得十分困难了。因此,我们建议在开发过程中应该避免滥用autorelease(),只在工厂方法等不得不用的情况下使用,尽量以release()来释放对象引用。

2、cocos的渲染机制

  3.x之前是通过每一个node的draw方法来使用OpenGl Es 代码进行渲染。

  3.x之后则使用了新的渲染机制,统一把所有的需要渲染的node安放在一个列队中再进行自动批处理渲染。

  主线程每一帧在Application::getInstance()->run()的过程中会调用mainloop()方法,mainloop()方法中会调用一次drawScene()方法

  

 

   方法名是drawScene,那么重点肯定就是对当前场景需要渲染的节点进行渲染。先清除渲染状态,然后调用render()方法。

  

 

   接下来我们看render函数具体是怎么实现的:它首先是使用visit方法让需要被渲染的节点进行排序并插入到渲染队列CommandQueue中,然后在一起自动批处理进行渲染。

    

  我们接着看visit方法,首先它根据localZOrder使用sortAllChildren()来进行排序。

  

 

   紧接着对localZOrder < 0的结点进行递归渲染。

  

 

   然后是渲染本身节点,最后在递归渲染localZOrder>0的子节点。其实本质就是按照(左,中,右)中序遍历进行渲染

  

 

 

  往下看每个节点的draw函数,我们可以发现,3.x之后没有直接在draw函数中进行渲染,而是把渲染命令压入到渲染序列中去,最后再回到render函数中进行自动批处理渲染。

  void Sprite::draw(Renderer * Renderer ,const Mat4 &transform,uint32_t flags){

    // 判定纹理是否有效

     if (_texture == nullptr) { return; }

     #if CC_USE_CULLING // Don't calculate the culling if the transform was not updated

     auto visitingCamera = Camera::getVisitingCamera();

    auto defaultCamera = Camera::getDefaultCamera();

     if (visitingCamera == defaultCamera) {

      _insideBounds = ((flags & FLAGS_TRANSFORM_DIRTY) || visitingCamera->isViewProjectionUpdated()) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;

    } else {

      //xxx:this always return true since

       _insideBounds = renderer->checkVisibility(transform, _contentSize); }

      // 判定渲染的纹理是否在可见区域内

      if(_insideBounds)

      #endif {

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

        // 将绘制命令添加到renderer绘制栈RenderQueue中

        renderer->addCommand(&_trianglesCommand);

         }

      }

    }

  }

  在render中,对列队中需要渲染的结点根据其GlobalZorder进行排序,

  globalZOrder是一个float(不是int)的参数,这个值在渲染器中用来给RenderCommand排序。

  较低的值拥有较高的优先级。这意味着一个globalZOrder为-10的节点会比一个globalZorder为10的 节点优先绘制。

  globalZOrder为0(默认值)的节点将会根据Scene Graph 顺序 绘制。

  

 

  然后再对列队中渲染命令调用OpenGl的API进行渲染。自此完成了整个渲染流程。

  总结来说:

  导演类中的mainLoop中会调用drawScene,

  在drawScene中会调用场景类的render,

  render中会递归执行节点的visit,visit中会调用精灵类的draw,draw中会执行渲染类的addCommand。

  对所有节点执行完addCommand后,会执行渲染类processRenderCommand,接下来执行渲染类的drawBatchedTriangles,

  最终drawBatchedTriangles内会调用多个OpenGL API完成渲染。

3、cocos的cache缓冲区

  TextureCache:加载大纹理图如 背景图片(pkm,png)

  Director::getInstance()->getTextureCache()->addImage(filename)

  // 存入

   Texture2D *texture = Director::getInstance()->getTextureCache()->getTextureForKey(textureKeyName)

  // 使用

  Director::getInstance()->getTextureCache()->removeUnusedTextures()

   // 清理没被引用的

  Director::getInstance()->getTextureCache()->removeAllTextures() // 清理全部

  SpriteFrameCache.:加载碎的精灵帧图片,plist文件

  texture)//清理某图片

posted @ 2022-07-04 10:53  jiaxin2015  阅读(216)  评论(0编辑  收藏  举报