热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Starling性能优化技巧十五则

Starling的性能优化要点一、尽可能减少状态变更如您所知,Starling使用Stage3D来渲染所有的可见对象。这就意味着所有的绘制都是GPU完成的。现在&#x

Starling的性能优化要点

一、尽可能减少状态变更
 

如您所知,Starling使用Stage3D来渲染所有的可见对象。这就意味着所有的绘制都是GPU完成的。

现在,Starling可以一个接一个的发送四边形到GPU,然后一个接一个的绘制。实际上,这也是最初版本的Starling的工作方式。然而,为了更好的执行效率,GPU希望能得到大量的数据,然后在一次调用中绘制所有的对象。

这也是为什么Starling的最新版本要在发送数据到GPU之前要包含尽可能多的四边形。然而它只能批量处理那些拥有相似属性的四边形。每当遇到一个具备不同“状态”的四边形,就会触发“状态改变”,上一个批次的四边形就会被绘制。
 

这篇文章中,我将使用“Quad”和“Image”来代表相似的概念。请记住,Image只是Quad的一个添加了纹理的子类。

下面这些是促使状态发生改变的关键特性:



  • 纹理 (虽然从相同的图集中获得不同的纹理是可以的)
     
  • 显示对象的混合模式
     
  • 图片的平滑设置
     
  • 纹理的平铺模式
     
  • 四边形的着色属性 (下面提到的)
     


如果您可以用一种尽可能减少状态变化的方式创建场景,那么您的渲染性能将会受益无穷。

二、着色四边形
 

一些移动设备硬件(比如第一代的iPad)处理纹理的“着色”时非常困难,包括:



  • 用半透明的方式去绘制它们(alpha值是其中之一)
     
  • 用不同的颜色来绘制(设置image.color等于一些不是白色的色值)
     


由于这个原因,Starling优化了未着色的图片的渲染代码。但这样带来了一个缺点:在未着色的对象和着色的对象之间切换时,将会导致一次状态变更。在您设置图片的颜色或透明度的时候,请牢记这一点。

如果您创建了一个硬件加速的游戏,并且没有考虑到着色这个问题,那么这些状态变更可能给您的性能带来不必要的麻烦。

 

这里有个简单的技巧来避免状态变更:只需要设置您的根对象(root object)的透明度为“0.999”或一个近似的值。由于这个透明度会向下影响到子元件的渲染,Starling会将任何对象都按照“着色”对象来对待,这样就不会触发过多的状态的变更了。


三、画家算法

要了解如何尽量减少状态变更,您需要了解Starling处理您的对象的顺序。
 

和传统Flash类似,Starling也采用了一种“画家算法”来处理显示列表。这个算法的意思就是,就像一个画家所做的那样,来绘制您的场景:首先绘制最底部的对象(比如一个图片背景),然后再绘制上一层的对象(以此类推)。
 

如上图所示,如果您想在Starling中创建这样的场景,您可以创建3个Sprite容器:一个用来包含远处的山脉,一个包含地面,一个包含植被。山脉的排序肯定在最底部(索引是0),植被的排序是在最顶部(排序是2)。每一个Sprite会包含有实际内容的图片。
 

在渲染的时候,Starling会先渲染最左侧的”Mountain 1″,并向右继续渲染,直到到达 “Tree 2”。现在,如果所有的对象都有不同的状态,那么就会造成6次绘制调用。如果您从每一个单独的图像来加载每一个对象的纹理,就会发生这样的情况。


四、纹理图集
 

这就是纹理图集是如此重要的原因之一。如果从一个单一的图集加载所有这些纹理,Starling就可以在一次调用中完成所有的绘制! (假如上面列出的其他属性没有改变的话。)
 

在这里,每个图像都使用相同的图集(用所有节点具有相同的颜色来表示)。这样的后果就是您应该总是让您的所有纹理使用同一个纹理图集。

 

然后,有时候,并非所有的纹理都能融合到一个单一的图集上。一个纹理图集的限制是2048*2048像素(这是一些移动设备硬件的限制),所以您早晚都会碰到这种所需纹理为空的情况。但是这也没什么问题—只要您用一种聪明的方式来安排您的纹理。

 

如上图所示,这两个例子都是使用了两个纹理图集(同样,每一个纹理图集用一个颜色表示)。但是左侧的显示列表,将强迫每个对象发生状态改变,而右侧的版本则可以只需要两次批处理就能绘制所有的对象。


五、扁平化的Sprites


通过减少状态变更,您已经让您的游戏性能得到了极大改善。然而,Starling还是需要遍历所有的对象,检查它们的状态,然后上传它们的数据到GPU—在每一帧都是如此。


这是下一步优化要做的内容。如果您的游戏中有一些内容是静态的,并且不会发生(或很少)改变,就可以调用这个Sprite容器的 flatten方法(暂且翻译为扁平化吧)。Starling将会预处理它的子元件,并上传它们的数据到GPU。在后续的帧中,它们就可以马上被呈现,而 且不需要任何额外的GPU处理,也无需向GPU上传新的数据。


这是一个强大的特性,可以极大地减少CPU的负载。您只需注意,每一个扁平化的容器,仍然会被状态变化影响:如果一个扁平化容器的几何数据包含了一些不同的渲染状态,它仍然会在多个步骤进行绘制。


六、QuadBatch 类


扁平化的容器非常快速和容易使用。然而,它们仍然有一些开销:



  • 当您在Sprite上添加了一个对象,它们将派发”ADDED”和”ADDED_TO_STAGE”事件,如果有很多子元件需要添加,这可能也是不小的开销。
     
  • 对于任何的显示对象容器来说,一个特定的子元件只能添加一次。
     

为了摆脱这些限制,您可以去使用Starling的一个底层类:QuadBatch。它的工作原理是这样的:


var quadBatch:QuadBatch = new QuadBatch(); 
var image:Image = new Image(texture); 
quadBatch.addImage(image); 
   
for (var i:int&#61;0; i<100; &#43;&#43;i) 

    image.x &#43;&#61; 10; 
    quadBatch.addImage(image); 
}

 

您是不是已经注意到了&#xff1f;如果您愿意&#xff0c;您可以重复添加相同的图像&#xff01;此外&#xff0c;它不会引发任何事件调度。然而事务都有两面性&#xff0c;这

样做也有一些缺点&#xff1a;

 



  • 您添加的所有的对象必须具备相同的状态&#xff08;比如&#xff1a;使用同一个纹理图集&#xff09;。您添加到QuadBatch的第一个图像决定了它的状态。您不能再改变状态&#xff0c;除非完全重置这个QuadBatch。 
     
  • 您只能添加Image, Quad, 或 QuadBatch类的实例.
     
  • 这是一条单行道&#xff1a;您只能添加对象。删除一个对象的唯一途径是重置当前批次。

    由于这些原因&#xff0c;它仅适用于一些特定的场景&#xff08;比如位图字体类&#xff0c;就直接使用了四边形批次&#xff09;。在这些情况下&#xff0c;它肯定是最快的选择。您将找不到一个更有效的方式来呈现Starling对象。

    七、使用位图字体

     

文本框支持两种不同的字体&#xff1a;True Type字体和位图字体。

TrueType字体最容易使用&#xff1a;只需嵌入所需的“ttf”文件&#xff0c;您就大功告成了。为静态文本框包含数百个字符&#xff0c;这是一个很好的和快速的选 择。Starling会将文本渲染成位图&#xff0c;显示文本的时候就像一个纹理一样。对于重复改变的短文本&#xff08;比如分行显示&#xff09;&#xff0c;这样处理还是挺慢的。

 

如果您的游戏需要显示的文本中包含许多非ASCII字符&#xff08;如中文或阿拉伯文&#xff09;&#xff0c;TrueType字体可能是您唯一的选择。位图文本只是受限于它们的纹理大小。
 

 

使用位图字体的文本框&#xff0c;创建和更新都是非常快速的。另一个优点是&#xff0c;它们不会占用任何额外的纹理内存&#xff0c;除了它们所需的原始纹理。这是在Starling中显示文本的首选方式&#xff0c;我的建议是尽可能的使用它们。


八、使用BlendMode.NONE

 

如果您有完全不透明的矩形纹理&#xff0c;可以帮助GPU禁用那些纹理混合。这对于大背景图像特别有用。不要害怕这将导致额外的状态变化&#xff0c;这是值得的&#xff01;
 

 


backgroundImage.blendMode &#61; BlendMode.NONE; 



九、使用Stage.color

如果您的游戏背景是一个单独的颜色&#xff0c;请设置stage的颜色来代替添加一个纹理或一个着色的四边形。反正Starling每一帧都要做stage的清理工作—如果您改变了stage的颜色&#xff0c;那么也没有额外的消耗。这简直就是免费的午餐哦&#xff0c;记得享用。

 


[SWF(backgroundColor&#61;"#ff2255")] public class Startup extends Sprite { // ... } 

 

十、避免重复调用widthheight

获取宽度和高度属性是一个昂贵的性能开销&#xff0c;特别是对于Sprite容器&#xff08;首先矩阵进行计算&#xff0c;然后每一个子元件的每个顶点都和该矩阵相乘&#xff09;。

出于这个原因&#xff0c;请避免重复访问它们&#xff0c;比如在一个循环里面。在某些情况下&#xff0c;使用一个恒定的值来代替它们更有意义。


// 坏方案: 
for (var i:int&#61;0; i

    var child:DisplayObject &#61; getChildAt(i); 
    if (child.x > wall.width) 
        child.removeFromParent(); 

   
// 好方案: 
var wallWidth:Number &#61; wall.width; 
for (var i:int&#61;0; i

    var child:DisplayObject &#61; getChildAt(i); 
    if (child.x > wallWidth) 
        child.removeFromParent(); 
}

 


十一、让容器不可点击

当您在屏幕上移动您的光标/手指的时候&#xff0c;Starling就会寻找哪一个对象被点击了。这可能是一项昂贵的操作&#xff0c;因为它需要遍历所有的显示对象&#xff0c;并调用hitTest方法。

因此&#xff0c;如果您不需要一个对象被触碰&#xff0c;将它设置为“untouchable”是非常有帮助的。最好是在容器上进行禁用&#xff1a;这样&#xff0c;Starling就不会遍历它的子元件。


// 好方案: 
for (var i:int&#61;0; i
    containter.getChildAt(i).touchable &#61; false; 
   
// 更好的方案: 
container.touchable &#61; false;
 

 

十二、使用新的事件模型

从Starling 1.2开始, 有一个新的方法来派发事件:


// 传统方式: 
object.dispatchEvent(new Event("type", bubbles)); 
   
// 新方式: 
object.dispatchEventWith("type", bubbles);
 

像第一个传统的方式那样&#xff0c;第二种方法也会派发一个事件对象&#xff0c;但是在屏幕背后&#xff0c;它会用对象池来缓存事件对象。这就意味着&#xff0c;如果您使用第二种方式&#xff0c;将 会节省一些垃圾回收器工作的时间。由于它书写简练并且速度更快—因此&#xff0c;它是现在派发事件的首选方式。&#xff08;如果您已经创建了Event类的子类&#xff0c;就不能用 这个方法来派发事件&#xff09;


十三、ActionScript 指导

 

以下的优化方式是通用的&#xff08;并不特指Starling&#xff09;,对于所有的ActionScript项目都是最佳实践。但是别太高估它们&#xff1a;您的首要任务是保证代码的优良结构和可读性。这些优化在一些每帧都调用的代码中最有效。

循环

避免“for each”. 用传统的 “for i”是最快的. 此外需要注意的是&#xff0c;将一些变量事先保存&#xff0c;在每次循环中调用&#xff0c;也是非常有用的。

// 慢的: 
for each (var item:Object in array) { ... } 
   
// 快的: 
for (var i:int&#61;0; i
   
// 更快的: 
var length:int &#61; array.length; 
for (var i:int&#61;0; i


 

十四、避免创建对象

避免产生大量的临时对象。它们占用内存&#xff0c;并且需要由垃圾收集器进行清理&#xff0c;这可能会导致运行时的”卡壳”。



// 坏的: 
for (var i:int&#61;0; i<10; &#43;&#43;i) 

    var point:Point &#61; new Point(i, 2*i); 
    doSomethingWith(point); 

   
// 好的: 
var point:Point &#61; new Point(); 
for (var i:int&#61;0; i<10; &#43;&#43;i) 

    point.setTo(i, 2*i); 
    doSomethingWith(point); 
}
 


十五、访问数组或向量数组元素

当您从一个数组或一个向量数组中引用一个对象的时候&#xff0c;要小心&#xff1a;当对象索引是一个计算结果的时候&#xff0c;请转换为int类型。出于某种原因&#xff0c;这样可以让AS3计算更快。


// 坏的: 
var element:Object &#61; array[10*x]; 
   
// 好的: 
var element:Object &#61; array[int(10*x)];


点击打开链接


推荐阅读
  • c语言 怎么访问64位地址_C语言调动硬件的原理是什么?
    大家都知道我们可以使用C语言写一段程序来控制硬件工作,但你知道其工作原理吗?1c语言在实际运行中,都是以汇编指令的方式运行的,由编译器把C ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 本文介绍了Linux Shell中括号和整数扩展的使用方法,包括命令组、命令替换、初始化数组以及算术表达式和逻辑判断的相关内容。括号中的命令将会在新开的子shell中顺序执行,括号中的变量不能被脚本余下的部分使用。命令替换可以用于将命令的标准输出作为另一个命令的输入。括号中的运算符和表达式符合C语言运算规则,可以用在整数扩展中进行算术计算和逻辑判断。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 本文介绍了2020年计算机二级MSOffice的选择习题及答案,详细解析了操作系统的五大功能模块,包括处理器管理、作业管理、存储器管理、设备管理和文件管理。同时,还解答了算法的有穷性的含义。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 目录浏览漏洞与目录遍历漏洞的危害及修复方法
    本文讨论了目录浏览漏洞与目录遍历漏洞的危害,包括网站结构暴露、隐秘文件访问等。同时介绍了检测方法,如使用漏洞扫描器和搜索关键词。最后提供了针对常见中间件的修复方式,包括关闭目录浏览功能。对于保护网站安全具有一定的参考价值。 ... [详细]
  • 查找给定字符串的所有不同回文子字符串原文:https://www ... [详细]
  • 交换机配置:intg100unshintvlani1ipadd192.168.56.177qstelseuser-iv4authaaaproinsshupl3qsshuserpyt ... [详细]
  • 深入理解计算机系统之链接(一)
    程序是怎样运行的写好的c程序怎样运行的呢?答案是一个写好的程序要先经过语言预处理器,编译器,汇编器和链接器生成最后的可执行文件,然后加载器将可执行文件加载到内存中才能运行。这里以一 ... [详细]
author-avatar
前所未闻啊_549
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有