Egret源码分析-渲染

前言

在了解了主循环之后,接下去看的就是引擎的渲染部分~

RenderCommand

首先要提的是RenderCommand这个类,它主要是将每个对象的渲染函数进行封装,并加到渲染队列里面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
var RenderCommand = (function () {
function RenderCommand() {
}
/*
在MainContext.prototype._draw方法中,
会遍历__DRAW_COMMAND_LIST中的对象,并调用对象的该方法
*/
RenderCommand.prototype.call = function (renderContext) {
var o = this;
if (o.callback) {
o.callback.call(o.thisObject, renderContext);
}
};
// 清除命令,__freeList:用于缓存对象,避免不断创建
RenderCommand.prototype.dispose = function () {
this.callback = null;
this.thisObject = null;
RenderCommand.__freeList.push(this);
};
/*
将一个渲染命令加入到__DRAW_COMMAND_LIST队列里面。
该方法会在DisplayObject.prototype._updateTransform方法中调用
*/
RenderCommand.push = function (callback, thisObject) {
var cmd;
if (RenderCommand.__freeList.length) {
cmd = RenderCommand.__freeList.pop();
}
else {
cmd = new egret.RenderCommand();
}
cmd.callback = callback;
cmd.thisObject = thisObject;
egret.MainContext.__DRAW_COMMAND_LIST.push(cmd);
};
RenderCommand.__freeList = [];
return RenderCommand;
})();
egret.RenderCommand = RenderCommand;

渲染流程

渲染流程的主要代码都在这两个函数里面。

  1. MainContext.prototype.renderLoop
  2. MainContext.prototype._draw
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    MainContext.prototype.renderLoop = function (frameTime) {
    ...
    // HTML5CanvasRenderer 对象
    var context = this.rendererContext;
    context.onRenderStart();
    context.clearScreen();
    // 清空渲染队列
    MainContext.__DRAW_COMMAND_LIST = [];
    MainContext._renderLoopPhase = "updateTransform";
    // 会依次遍历子节点的_updateTransform方法,并添加渲染命令RenderCommand,到队列中
    stage._updateTransform();
    ...
    // 执行渲染命令
    this._draw(context);
    ...
    };
    MainContext.prototype._draw = function (context) {
    var list = MainContext.__DRAW_COMMAND_LIST;
    var length = list.length;
    // 遍历所有的渲染命令RenderCommand,调用它们的call方法
    for (var i = 0; i < length; i++) {
    var cmd = list[i];
    cmd.call(context);
    cmd.dispose();
    }
    };

DisplayObjectContainer._updateTransform方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
DisplayObjectContainer.prototype._updateTransform = function () {
var o = this;
if (!o._visible) {
return;
}
// 将(保存渲染该对象时的参数和状态的操作)加到渲染队列中
// 渲染命令通过队列的结构来执行
// 具体命令的执行函数(_pushMask)通过堆栈的结构来保存,方便子节点的各个参数计算
if (o._filter) {
egret.RenderCommand.push(this._setGlobalFilter, this);
}
if (o._colorTransform) {
egret.RenderCommand.push(this._setGlobalColorTransform, this);
}
var mask = o.mask || o._scrollRect;
if (mask) {
egret.RenderCommand.push(this._pushMask, this);
}
// 调用父类的_updateTransform
_super.prototype._updateTransform.call(this);
// 调用子节点的_updateTransform
// 采用先序遍历,先计算父节点的状态矩阵(坐标,缩放,位移),再计算子节点的状态矩阵
if (!this["_cacheAsBitmap"] || !this._texture_to_render) {
for (var i = 0, length = o._children.length; i < length; i++) {
var child = o._children[i];
child._updateTransform();
}
}
// 将(移除渲染该对象时的参数和状态的操作)加到渲染队列中
// 这一步实际上做的是出栈的操作
if (mask) {
egret.RenderCommand.push(this._popMask, this);
}
if (o._colorTransform) {
egret.RenderCommand.push(this._removeGlobalColorTransform, this);
}
if (o._filter) {
egret.RenderCommand.push(this._removeGlobalFilter, this);
}
};

DisplayObject._updateTransform && DisplayObject._draw方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
DisplayObject.prototype._updateTransform = function () {
var o = this;
if (!o._visible) {
return;
}
// 这里做矩阵运算
o._calculateWorldTransform();
if (egret.MainContext._renderLoopPhase == "updateTransform") {
if (o.needDraw || o._texture_to_render || o._cacheAsBitmap) {
// RenderCommand里面的call方法,所调用的就是对象的_draw方法
egret.RenderCommand.push(this._draw, this);
}
}
};

DisplayObject.prototype._draw

1
2
3
4
5
6
7
8
DisplayObject.prototype._draw = function (renderContext) {
//各种堆栈的push操作
...
// 在_draw中又调用了_render方法
this._render(renderContext);
//各种堆栈的push操作
...
};

从这个方法看到,真正的渲染都放在_render里面,这也是官方文档给出 下面这些提示的原因。

1
2
3
4
5
6
* 任何继承自DisplayObject的类都必须实现以下方法
* _render();
* _measureBounds()
* 不允许重写以下方法
* _draw();
* getBounds();

小结

整个渲染的流程,差不多就是这个样子。Bitmap、Sprite、MovieClip之类的对象,它们的_render方法都不大一样,这里就不展开讨论了。大家可以自己去看它们的js源码。

转载本站文章请注明作者(xtutu)和出处 xtutu.me