原文:https://blog.csdn.net/u010223072/article/details/78287294
理论要点
要点一:
文件格式与像素格式的区别:文件格式是图像为了存储信息而使用的对信息的特殊编码方式,大都经过了压缩,它存储在磁盘或内存中,但是并不能被GPU所识别(jpg,png…),这些图片格式当被游戏读入后,还需要经过CPU解压成像素格式,如:RGBA8888,再传送到GPU端进行使用。
而像素格式是能被GPU所识别的,能被快速寻址并采样。
要点二:
图片本身大小和其所占内存大小是两码事。这就好比一个zip压缩包使用时要解压还原数据,这个zip文件就好比图片本身,而解压后的文件就好比图片所占内存大小。纹理本身大小主要看我们选用什么样的压缩格式和压缩比,而所占内存大小就只由两个因素决定:1,图片的像素点个数(分辨率) 2,单位像素占用的字节数(像素格式)。即纹理内存大小 = 纹理长度 * 纹理宽度 * 单位像素占用的字节数,如:一张1136x640的RGBA8888的png图片占用的内存为 1136x640x4 = 2.8M左右。
注:cocos引擎中,对于大的背景图我们一般使用jpg文件格式,jpg压缩比更大,是有损压缩,而像素格式使用16位的RGB565格式。但是它占内存大小就不是我们使用的RGB565 16位格式了,cocos会自动把它转换为RGBA8888 32位格式解析。最终决定图片占用内存的是它的像素格式和尺寸,与其扩展名无关。png8、png32、jpg、pvr只要其像素格式都是rgba8888,那么最终图片占用的内存是一样的。
要点三:
在cocos中,谈谈不同纹理实际加载过程中内存变化,先以一张1024*1024 本身大小500k的jpg/png图片为例:
1,读取图片文件(500k)
2,把jpg/png数据最终都是按RGBA8888(默认)格式解析(4mb)
3,释放500k的图片内存
4,上传给OpenGL纹理数据(4mb)
5,释放4mb内存
注意,这个过程不是必然的顺序执行,释放内存实际是由系统决定的,会很快,但是不一定是立即执行。 所以内存会瞬间飙升到8.5mb左右,然后减少5mb,稳定到4mb左右,这么一个过程变化。这就是我们常说的“间歇性内存飙高”。
而pvr格式显卡直接支持,加载速度自然要快,不需要开辟临时内存来读取pvr图片,节约了解析图片数据到纹理这一步的消耗(第2步)。也就是说读取同样像素格式的pvr资源(一般用pvr.ccz压缩格式)消耗4mb,将pvr图片数据提交给显卡消耗4mb。然后释放文件数据4mb。这么看似乎跟Png从内存占用上相比也没什么优势。但是如果像素格式用的是RGBA4444那就有很大优势了,内存少一半。而前面的那种自身不管用什么格式,最终cocos都会把它们的格式统一默认转换为32位的RGBA8888再上传GPU。
注释:
1,pvr.ccz其实就是pvr图片zip打包下,程序读的时候要先解压出pvr资源,然后再读取pvr。不过由于压缩下可以极大的减小图片体积,所以虽然多了解压过程也不会有特别多的cpu消耗,一般我们都是推荐选择pvr.ccz的。
2,当加载jpg、png这样的纹理时,在短时候内,它会消耗约两倍于(jpg3倍,多jpg到png转换过程)它本身内存占用的内存大小。这个告诉我们这样的纹理最好不要一帧内连续加载,最好分散到多帧去完成,因为每帧cocos都会自动回收一次内存,对于这些中间创建的纹理内存会自动释放,从而避免一帧里内存不至于飙太高。
3,按照纹理size从大到小的顺序加载纹理,由于加载纹理时额外的内存消耗问题,所以,采用按纹理size从大到小的方式来加载纹理是一个最佳实践。假设,你有一个占内存16MB的纹理和四个占用内存4MB的纹理。如果你首先加载4MB的纹理,这个程序将会使用16MB的内存,而当它加载第四张纹理的时候,短时间内会飙到20MB。这时,你要加载16MB的那个纹理了,内存会马上飙到48MB(4*4 + 16*2),然后再降到32MB(4*4 + 16)。但是,反过来,你先加载16MB的纹理,然后短时候内飙到32MB。然后又降到16MB。这时候,你再依次加载剩下的4个4MB的,这时,最多会彪到(4*3 + 4*2 + 16=36)MB。在这两种情况下,内存的峰值使用相差12MB,要知道,可能就是这12MB会断送你的游戏进程的小命。
如何减小资源占磁盘大小
1,jpg压缩比最高,质量较好,但是不支持半透明(一般用于背景图)。
2、png8同样图片会比jpg略大一些,使用ImageAlpha进行转换,视觉上几乎看不出差别。
注: 这两种图片格式都可以极大的减少图片体积(减少70%~80%),但是无助于减少内存。
如何减小资源占内存大小
1,尽量使用颜色深度为16bit的图片。cocos上通过cc.Texture2D.setDefaultAlphaPixelFormat(cc.Texture2D.PIXEL_FORMAT_RGBA4444)改变默认像素格式,但如果图片本身的颜色深度是32位,转换成RGBA4444后,则可能会使图片失真,看起来就很模糊。这个可以通过TexturePacker工具中开启抖动算法得到改善(模糊处颜色混合处理)。特别是在拥有Retina显示的像素密度下,你几乎看不出16位与32位的纹理之间的差别。
2,碎图打大图时,使用NPOT纹理,NOPT是“non power of two”的缩写,译作“不是2的幂”。如果纹理图集(texture atlas)使用NPOT的纹理,它将有一个具大的优势:它允许TexturePacker更好地压缩纹理。因此,我们会更少地浪费纹理图集的空白区域。而且,这样的纹理在加载的时候,会少使用1%到49%左右的内存。
3,使用pvr格式纹理。因为jpg是没有透明色的,一个像素最多3字节,而png一个像素4字节,jpg纹理应该占用内存更小才对,但是cocos最终把纹理都会转换成rgba8888格式,所以无论是jpg还是png,一个像素占用的都是4字节。正因cocos2d对其他纹理支持不够好,pvr才会显得那么高效。pvr也不是万金油。pvr图像是专门为ios设备上面的powerVR图形芯片指定的图形容器,可以直接加载到显卡上,而不需经过中间的转化。虽然android设备下可以使用pvr格式,但是不能使用pvrtc4(一个像素只占4bit),希望通过pvr像ios设备上一样真正减少游戏内存是不太可行的。
4,android上最省内存纹理当然是ETC(一个像素占4bit),不过ETC1没有alpha通道,需要我们额外通过一些简单shader实现(同样大小的遮罩图做颜色混合)。不过现在最新的ETC2可以直接支持alpha通道了,而且效果更好,但是需要opengles3.0支持,考虑到2.0设备的市场占有率,一般使用ETC1。
实际项目中纹理格式选择
人生之所以纠结,在于许多事情你可以选择。上面的纹理格式,同一种情况下,可能多种都适合,那如何选择呢!我们还是根据具体情况而定:
1、场景、背景、全屏图片
2D手机游戏中,多半都有这样的图片,以作为背景,特别在一些SLG,横版过关游戏中。这种图片对ALPHA没有要求,并且,在同一时间,只会出现一张(如果是多张拼接,也不会超过屏幕尺寸太多),内存不会成为关键点。所以,在这种情况下,我们大胆选择JPG就可以了。
2、场景的前景,装饰物,可移动对象(npc,moster,…)
这种要看规模,如果规模较小,类型不多。 或者类型虽然多,但同一时间出现在场景中的类型不多,那我们可以选择压缩PNG8的方式,它支持ALPHA通道,文件又小。如果同屏可能出现多种这种,则需要考虑在IOS上使用PVRTC,在ANDROID上使用ETC1+ALPHA_MASK。实际上,为了好维护,一般都是统一用pvr.ccz打包。
3、UI
UI的背景图,可以优先考虑使用压缩PNG8,如果达不到精度要求,则使用PNG32。而对于UI的小元素,可以考虑使用压缩PNG8.
对于UI的图标,一般是不带ALPHA的PVRTC/ETC + 一张公共的ALPHA掩码图,通过双层混合来实现圆边效果。 因为图标同屏出现可能较大。 如果图标能够控制在一定范围内,由于图标是48X48等大小,一张1024x1024的大图,可以放400个图标。 换用JPG,也有4MB的开销,如果这个是可以接受的,也可以使用JPG+ALPHA_MASK的方式。
写到这里才发现,其实只需要下面一句话就可以搞定。
这类图片会不会同时出现多个,同时出现时,内存开销是否无法接受, 如果确实无法接受,则使用GPU纹理,否则,优先考虑JPG,JPG+ALPHA,或者PNG8。就是说,首先要减小安装包大小,如果内存有无法接受的情况,才需要用GPU纹理进行优化。
经验总结
游戏内存优化我们一般可以从这么3个方面入手:引擎自身提供的优化选项,引擎底层框架,语言上层(内存泄漏)。
下面是一些常用手段:
首先看纹理优化,为了优化纹理内存使用,必须知道什么因素对纹理内存使用的影响最大。主要有3个因素会影响纹理内存,即纹理格式(压缩还是非压缩)、颜色深度和大小。我们可以使用PVR格式纹理减少内存使用。推荐纹理格式为pvr.ccz。纹理使用的每种颜色位数越多,图像质量越好,但是越耗内存。所以我们可以使用颜色深度为RGB4444的纹理代替RGB8888,这样内存消耗会降低一半。此外超大的纹理也会导致内存相关问题。所以最好使用中等大小的纹理。
音频优化,3个因素会影响音频文件的内存使用,即音频文件数据格式、比特率及采样率。推荐使用MP3数据格式的音频文件,因为Android平台和iOS平台均支持MP3格式,此外MP3格式经过压缩和硬件加速。背景音乐文件大小应该低于800KB,最简单的方法就是减少背景音乐时间然后重复播放。音频文件采样率大约在96-128kbps为佳,比特率44kHz就够了。
字体和粒子优化,在此有两条小提示:使用BMFont字体显示游戏分数时,请尽可能使用最少数量的文字。例如只想要显示单位数的数字,你可以移除所有字母。至于粒子,可以通过减少粒子数来降低内存使用。
提示与技巧:
1、一帧一帧载入游戏资源
2、减少绘制调用,打包大图
3、载入纹理时按照从大到小的顺序
4、避免高峰内存使用
5、使用载入屏幕预载入游戏资源
6、需要时释放空闲资源
7、收到内存警告后释放缓存资源
8、使用纹理打包器优化纹理大小、格式、颜色深度等
9、使用JPG格式要谨慎!
10、请使用RGB4444颜色深度16位纹理
11、请使用NPOT纹理,不要使用POT纹理
12、避免载入超大纹理
13、推荐1024*1024 NPOT pvr.ccz纹理集,而不要采用RAW PNG纹理