FlashPlayer9和AVM2的弹性跑道模型

2005年,Ted Patrick发布了一篇伟大的文章《elastic racetrack》。它充当了我多年的参考资料,用于理解一帧内的代码的执行和渲染是如何平稳的处理的。等到了FlashPlayer9和AVM2的面世,我发现弹性跑道模型有了一些变化。此信息是基于我对FashPlayer内部事件和渲染的研究,但整个模型并没有被Adobe公司的工程师证实。

弹性跑到模型的基础没变:以一个具体的帧率运作,FlashPlayer会用帧的第一部分来执行代码,第二部分渲染显示对象。每个部分都能增加它的跑道,以容纳更多的指令处理和有效的延长帧的持续时间。


跟以前的模型相比,这两个部分有什么变化,它们在一帧内又是如何相处的?

AVM2被一种我称之为Marshal的东西控制着,Marshal负责为FlashPlayer提供时间片(一秒钟执行的次数)。首先要说明的是这些时间片与帧率不是同个东西,接着我们会看到FlashPlayer如何在帧率下综合处理这些时间片的。在MacOSX的火狐浏览器中运行一个Flex程序,Marshal每19-20毫秒的执行一个时间片,但据我观察以及Adobe员工的暗示,这个时间在不同的操作系统和不同的浏览器下是不同的。同时也取决于swf文件是如何编译的,可以看一下下面的注解。为了方便计算,我们以20毫秒执行一个时间片来进行假设。这意味着Marshal每秒钟尝试生成和执行不超过50片,然后它可能会根据代码的执行和渲染弹性的减少。每个时间片都可能按下列5个步骤顺序处理。
1  FlashPlayer事件分发—包括定时器,鼠标,ENTER_FRAME,URLLoader等分发的事件……
2  用户代码执行--监听第一步分发的事件的代码在这个阶段执行。
3  渲染事件被调度—当stage.invalidate()执行时,这个特殊的事件被调度。
4  最后用户代码被执行—监听第3步的代码在这时候被执行。
5  FlashPlayer重新渲染改变显示列表。

Marshal在脚本运行的时候反复的飞速执行这个20毫秒片。时间片内的脚本处理将产生两个主要跑道(代码处理与渲染),这两个跑道组成了1帧。用户动作和失效的动作放在代码跑道上,而渲染动作则放在渲染跑道上。主要注意的是,这些脚本只会Marshal分配的时间内执行。所以,如果你只是执行一个很短的操作,Marshal也会等待几毫秒再执行失效的动作和渲染动作。

观察哪些动作在执行,弹性跑道如何创建的最好方法是看这些时间片在5帧,25帧,50帧下的表现。



正如你所看到的,弹性跑道每帧执行不同的动作,而FlashPlayer会根据帧率综合而成不同的视觉图像。所以在帧率为5的时候,每帧处理10个用户动作,1个失效动作,1个渲染动作。在帧率为25的时候,每帧处理2个用户动作,1个失效动作,1个渲染动作。在50帧的时候,每帧处理1个用户动作,1个失效动作,1个渲染动作。必须注意的是,有些事件只在某些片可用。例如,Event.ENTER_FRAME事件只会在一帧的第一个片中被分发。

那么,这意味着什么呢?请往下看。

1  代码执行时间或渲染时间过长,弹性跑道会延长一个20毫秒片的时间。弹性跑道就会决定,特定的片和帧的持续时间会不会因此而延长。Marshal可能会丢弃一些片,以保持真实的帧率接近编译的帧率。
2  真实的帧率不会超过Marshal所决定的帧率。你可以设置帧率为120,但是FlashPlayer最多执行50片,最多渲染50次。(或多或少由系统配置决定)。
3  代码可以比设定的帧率执行的更频繁。如果帧率设为1,那么Timer或者鼠标的事件会出现在每一片中(一秒50片),但只会在最后一片中执行。此外,只要你愿意,你还能用updateAfterEvent()加快执行屏幕上的渲染,但只有在响应鼠标,Timer或者键盘事件的时候才能调用此方法。但在这种情况下,Marshal会认为1帧结束了,然后在下一片中开始新的1帧。。最后,flash将会在鼠标移动任何Sprite造成一些属性(x,y,宽,高等)的改变时,会强制自动重新渲染屏幕。当然,这仍然出现在片末,并且预渲染逻辑仍然在执行。

4  并不是每帧执行相同的时间片,你的操作系统有可能因时间片的分配导致不规则的渲染。比如你的帧率为20,每秒执行50片(也就是2帧5片)。那么FlashPlayer将会每5个时间片渲染2帧,并将遵循3-2-3-2-3-2片的渲染比率。

这些结论是在我们假设的20毫秒片即每秒50次的执行中得出的结论。而事实上,经常有5毫秒或者100毫秒的误差。

如果你想自己测试这个模型,最简单的办法是搞两个swf文件,一个帧率为1,另外一个帧率为100,让他们共同执行一个Timer,时间间隔设置为1毫秒。通过Timer改变显示对象的x属性,然后用trace追踪不同的事件,比如鼠标,EnterFrame,渲染。在过去的2年里,我知道了还有很多关于FlashPlayer信息无法从测试结果中得到,它并不那么透明。

Flash8播放器模型:弹性跑道

欢迎加入这个弹性极大的跑道模型的讨论。

  我将Flash 播放器看作如一条跑道。跑道上有两个截然不同的部分,一个是处理ActionScript(他包括事件处理),一个是将内容表现到屏幕上。运行时,Flash播放器围绕着跑道按照SWF文档里指定的帧速前进。不管播放器被赋予什么样的指令,播放器都会尝试按预定的速度播放文档。指定的帧速即是跑道的最大速度,播放速度只能在此之下而不能超过这个限制值。

  播放器跑道模型

    


  SWF文档告诉播放器在规定的时间做相应的事。播放器处理ActionScript或显现到屏幕或两者同时发出的指令的时候,跑道就会被伸展。假如有许多的数据或影片剪辑要处理,或在跑道的单一环线中处理您添加的一切内容或指令,播放器会跳过需要他执行的那些指令,并会在最短的时间里完成一个循环,但不会比指定的帧速快。

  ActionScript 繁重时

    


  图像繁重时

    


  ActionScript and 图像都繁重时

    


  有混合这个模型的另外几个方面。首先,播放器会将图像元素构造到一个递减的目录树中。从基本的节点开始,有许多的以帧的内容和次级节点构成的分支。播放器每一个循环树目录上任何的元素都会被处理。当前的Flash 7播放器中,帧的内容按照需要从level0最高级开始依照从小到大的顺序被扫描和处理。根据图解表现关键是使树的层级和元素的内在需要相同的简单分明。树的菜单越少,层级越浅图像元素将会被更快速的显现到屏幕上。

  树的结构同样也适用于对ActionScript 字节码的处理当中。在AS阶段,树的现状被将以字节代码形式被扫描出来。每一个指定的事件将被逐级执行处理。ActionScript是基于栈的以栈为基础的,这样任何的字节代码被集中到堆栈上并被依照树的结构被线性处理。根据给出的帧上的代码和事件的容量,跑道上的AS部分将会被扩展。尽管没有一种固定的方法延迟代码的执行,播放器将会在每一个帧的循环中处理完被请求处理的任何内容。有趣的是,我们注意到那些使用功能和事件的代码能被排列成一个成的序列,等待着处理。在某种意义上,播放器从不会消损的,他会一直的做您所需要的任何工作,不会顺从于一个帧的循环。关键是不要在给出的帧上需要播放器处理太多的内容,并能对最新的帧依照逻辑和事件去处理。

  至于Flash 8,从播放器的Beta版的外观来看,cacheAsBitma允许图像作为位图存储为一个MC的分支。矢量图会以位图的形式储存,但要等到图片改变了才会渲染。很好的允许播放器集中的存储于播放器的不同区域,专注于矢量存储怎样被利用。在一个基于树的表现模型里,树的一些分支被配置成branch.cacheAsBitmap=true, 存储进程会在第一关创建一个位图并将重复利用这张位图直到本区域的矢量图标记为dirty。 同样,假如每一帧位图存储分支都被标记为dirty,您不得不强制播放器在每一个帧循环中产生一张位图(处理+存储)。在我的测试当中,最优化利用cacheAsBitmap并不像看起来那样简单。

  总之,Flash8播放器清楚的表明了他是两个不同元素的组合,图像处理和ActionScript 字节码处理。假如您滥用两者之中的一种或两种播放器的运行效率将会降低,导致下一个循环之前就认定一切已被完成。在更大的项目研发中,弹性跑道有助于帮助我们解释Flex和Flash发展中所表现的良好性能优化背后的一些晦涩难懂的逻辑。就像以前说过的许多次相同,“等那一帧”

你可能感兴趣的:(timer,Flash,存储,Adobe,actionscript,图像处理)