c++代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
void
CCSprite::renderToTex(
int
nWidth,
nHeight)
{
CCRenderTexture * pRenderTex = CCRenderTexture::create(nWidth,nHeight,
kCCTexture2DPixelFormat_RGBA8888,GL_DEPTH24_STENCIL8);
if
(pRenderTex)
{
pRenderTex->beginWithClear(0,0);
this
->visit();
pRenderTex->end();
->removeAllChildrenWithCleanup(
true
);
CCSprite * pRenderSprite = pRenderTex->getSprite();
(pRenderSprite)
{
initWithTexture(pRenderSprite->getTexture());
}
}
}
|
需要注意的坑:在小米3等手机使用CCRenderTexture出错,因为英伟达tegra4类型的gpu,glRenderbufferStorage分配渲染缓存时不支持GL_DEPTH24_STENCIL8,导致renderbuffer生成错误。解决方法: 通过glGetString(GL_EXTENSIONS)获取gpu信息。 如果不支持GL_DEPTH24_STENCIL8(24bits深度缓存+8bits的模板缓存,共享同一块renderbuffer) ,则分开创建depth和stencil缓存。 同时需要检测GL_OES_depth24,如果支持则使用GL_DEPTH_COMPONENT24_OES + GL_STENCIL_INDEX8,否则使用GL_DEPTH_COMPONENT16+GL_STENCIL_INDEX8。
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
GLenum _error_code = glGetError();
( _error_code != GL_NO_ERROR)
{
const
char
* extString = (
*)glGetString(GL_EXTENSIONS);
(
strstr
(extString,
"GL_OES_depth24"
) != 0)
{
glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT24_OES,(GLsizei)powW,(GLsizei)powH);
}
else
{
}
glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER,m_uDepthRenderBufffer);
(uDepthStencilFormat == GL_DEPTH24_STENCIL8)
{
glGenRenderbuffers(1,&m_uStencilRenderBufffer);
glBindRenderbuffer(GL_RENDERBUFFER,m_uStencilRenderBufffer);
_error_code = glGetError();
(_error_code == GL_NO_ERROR)
{
}
}
#endif
}
else
{
(uDepthStencilFormat == GL_DEPTH24_STENCIL8)
{
}
}
|
(3). 动态设置帧率
UI界面帧率设置最高30帧
战斗等特效和动画比较多的界面帧率设置最高60帧
(4). 自动裁剪
在一些大地图中,显示在屏幕外的纹理元素不添加到自动批渲染队里中。
(5).减少特效粒子数和帧数
把一些特效和帧动画改成骨骼动画,减少帧数量。
跟美术和策划沟通,在效果可接受的情况下减少特效粒子数。
2. 内存优化
由于我们游戏前期这方面做的工作不多,后期随着游戏内容的丰富,内存消耗越来越大,导致在一些低端机出现卡顿,切换到后台,进程经常被系统kill。优化方案如下:
(1)纹理压缩
压缩后的纹理直接使用gpu解码,不需要cpu解码展开再传输至gpu,极大减少内存,减少cpu计算,加快纹理加载速度。
格式:
android使用etc1,工具:Mali Texture Compression Tool
ios使用pvrtc4,工具:TexturePacker
内存占用:
ETC1占用内存跟图片尺寸有关,每个像素占0.5个字节,占用磁盘空间会比png8大。
PVRTC每个像素0.25~0.5个字节 (pvrtc2和pvrtc4)
其他常见纹理格式(2~4个字节)如图:
下面是在Android平台上纹理压缩实现步骤:
1. 使用TexturePacker把要压缩的散图,打包生成png和plist文件。
2. 使用pkm批处理工具,遍历和解析资源目录下的plist文件,根据plist中的png大图(textureFileName的值)生成同名pkm文件,这里需要注意的是生成pkm文件的时候根据情况选择是否创建带alpha部分,因为etc1本身不支持透明图片,使用mali工具生成一张拼接在一起的纹理,这张纹理上半部分是原始图片(无alpha信息),下半部分是alpha信息图片。带有alpha信息的pkm
图片在渲染的时候使用特殊的shader进行渲染,后缀名使用.pkm,不带alpha信息的图片后缀使用.pkm0。
带alpha信息
etcpack.exe %1 %2 -v -c etc1 -aa -s fast
不带alpha信息
etcpack.exe %1 %2 -v -c etc1 -s fast
3. 编写顶点shader和片段shader,在顶点着色器中得到原图纹理坐标和带有alpha信息的纹理坐标,然后在片段着色器中对纹理进行采样,预乘alpha。
4. 使用shader,因为游戏中有些图片没有进行纹理压缩,所以这里要在精灵类的initWithTexture函数中根据纹理名后缀进行区分使用哪个shader,在CCNodeLoader::parsePropTypeSpriteFrame)ccb加载的时候也需要根据是否有plist文件判断使用哪个shader。
5. 把plist中key为textureFileName的值的后缀由png替换成pkm或pkm0。
(2)使用texturepacker拼图
用texturepacker把一些小散图打包到一张大图,减少纹理IO和draw call。
(3)使用对象池
在需要频繁创建对象的场景中,使用对象池。
(4)UI(button,非渐变背景)使用九宫格
(5) 切换场景时释放无用纹理
1
2
CCTextureCache:sharedTextureCache():removeUnusedTextures();
CCTextureCache:sharedTextureCache():dumpCachedTextureInfo();
|
(6)整理常驻内存UI资源
把主界面底部共用菜单等界面资源打包成一张纹理,常驻内存,省去加载和重新创建纹理的开销。
3. 遇到的坑
(1) 游戏使用luajit后,crash率上升了不少。
原因:因为luajit不支持c++方式编译。在lua的c代码中,如果遇到错误会使用luaL_error来报错。通常lua是以c的方式来编译的,luaL_error最终会调用longjump来实现函数远程跳转,而这种调转不遵循c++关于stack unwinding的规范,cocos2dx是使用c++语言编写的。这个会直接导致在调了luaL_error之后对象不会被析构(一般会在对象的析构函数中做一些处理,如重置状态和释放资源等)。在对这个没有被析构的对象上循环执行一些图像操作时可能会导致crash。分析游戏crash上报日志发现,多数crash发生在MTK芯片设备上的/system/vendor/lib/egl/libGLESv2_mtk.so模块中和一些opengl操作上。
解决方案:使用官方版的luac替代luajit,并使用c++的方式编译。
http://codingnow.cn/cocos2d-x/1658.html原文