虚拟形象渲染任务调度算法
由于公司结构变动,需要接手之前组长的虚拟形象渲染算法,在稍作改动迁移至小程序之后,这里我主要修改的部分并不是渲染算法,而是在大列表情况下渲染任务的调度实现。
UPDATE:
继以实现的虚拟形象渲染算法,新增缓存、优先级等功能以实现优化。
调度模型
首先我们需要先明白实现一个虚拟形象,需要被控制调度的任务是 medel 局部图片的下载工作。
因此在商品列表页、专题页等会出现多个 medel 的场景下,我预想的调度模型如下图:
这是一个嵌套双队列的任务调度模型,最外层是当前场景下需要渲染的任务,每个渲染任务里面又必须依赖多个图片资源。
渲染任务队列
废话不多说,先上代码
|
|
|
|
以上就是渲染任务队列类和渲染任务类的代码,这里的调度机制是,当有任务被添加到队列时,会检查当前是否有正在执行的任务,如果没有就调出队首的任务开始执行,否则就等待当前任务执行完毕后再调出队首任务执行。
这里需要注意一点,添加任务的方式可能是推入一个任务,也有可能是一次性推入一个列表的渲染任务,所以需要区分当前推入的是一个对象还是数组,在区分数据是对象还是数组时,通常的做法是使用 Object.prototype.toString.call
,但是如果数据是用户自定义数据时,Object.prototype.toString.call(data)
会输出 [object, Undefined]
。
一个任务模型里面会包含一个渲染算法的实例,渲染算法里面就包含了图片下载任务队列。
图片资源下载任务队列
图片资源下载任务队列的健壮性要比渲染任务队列更高,除了普通队列的实现,还带有缓存图片的功能。
|
|
整体上和渲染任务队列实现是一致的,不过在此基础上还添加了缓存功能,用户缓存图片资源,这里简要说明一下,由于一个用户在访问一个带有 medel 的商品列表时,每个商品都要携带一个用户的 medel。因为每个 medel 都由 12 - 15 张局部图片组成,其中部分图片是重复的,比如虚拟形象的腿、胳膊等,因此会以图片的 url 为 key,将下载到本地的地址为 value 缓存起来。
这里需要注意一点就是,在浏览器中,如果一张图片资源正在下载中,又去下载同一张图片,浏览器是会正常下载的,因此我们需要将执行任务定义为一个 Set 实现去重。同时将任务完成后的回调以数组形式保存起来,然后依次执行。
|
|
这里就是缓存实现,在缓存队列里面,我们做了容量大小的限制,以及优先级的控制。
总结
以上就是在虚拟形象生成算法里面的两个任务调度算法,代码的健壮性和性能的优化还需要更加深挖,下一步的任务是在渲染队列上添加缓存以及优先级的功能,将普通的队列结构替换成优先队列结构。
优化
这里主要是实现两个方面的优化:
- 已渲染过的 medel,没必要二次渲染,虽然图片下载会有缓存,但是调用 canvas 本身就会有一定的开销,我们可以将已渲染好的 medel 的本地图片地址缓存起来。
- 支持优先级,这一部分主要为了满足一定业务场景下的需求,当用户从微信公众号的推广软文进入小程序,滚动条的初始位置会定位到指定的商品,这时候应该优先渲染该商品的 medel。
缓存
|
|
这里缓存我们可以复用之前图片缓存的 cacheManager,这里需要单例化我们的缓存管理对象,因为 canvas 渲染在不同页面需要用到的是同一个缓存管理对象。
配合缓存管理对象,在渲染管理对象的代码上也需要做一些改动:
|
|
以上代码的主要思路是:我们在渲染任务添加进队列时就去检索缓存 Map 里面是否存在可用缓存图片,如果存在直接推出该任务,不再进入任务缓冲池中。当一个任务渲染完成之后,会以图片的本地地址作为 value,指定的 hash 值作为 key,存入缓存管理对象中。
优先级
|
|
我们允许一个任务对象有 priority 属性,该属性值表示这个任务的优先级。在从任务缓冲池中推出任务时,优先推出优先级较高的任务,其次才是根据队列或者栈的方式推出对首或队尾的任务。
作者: Listen
来源: http://swarosky44.github.io
链接: http://swarosky44.github.io/2018/07/15/虚拟形象渲染算法/
本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可