文:shirly(腾讯云前端开发高级工程师)
《Web端AR美颜特效技术实现》一文中,我们探讨了Web端AR功能的一些技术实现。
Web技术在XR领域最多被诟病的缺陷在于其性能瓶颈,我们在实际的开发过程中也遇到了一些性能问题。在本文中将以这些技术为基础探讨如何在Web端的AR应用里进行性能优化,以实现更快的渲染速度、更高的渲染帧率
瓶颈分析
  1. 线程阻塞

JavaScript是单线程语言,所有的任务都在一个线程上完成,一个任务排着一个任务执行。这就意味着,当线程繁忙时,很多任务都会被阻塞,在前端的体验上就是卡顿。
而作为一个提供AR渲染的SDK,实时渲染意味着每一帧都会进行一次检测与渲染刷新,尽管检测使用了WebAssembly技术来提速,但是WebAssembly的执行依旧是阻塞主线程的。

可以看到在跑满60fps的状态下,检测+渲染几乎占满了整个线程,这就意味着当有客户接入了SDK并实现了一些高频的业务交互后,可能会因为线程的阻塞而导致响应延迟等问题。
  1. 渲染帧率

GPU加速的WebGL渲染性能是非常好的,可以在毫秒级别完成一次draw call,同时方案中采用的渲染库对draw call做了极致化的减少。但在实际的性能观测过程中,还是发现帧率会随着渲染纹理的增多以及着色器的复杂有所降低,在排查之后发现是AI检测的控制框架,由于要在WebAssembly和WebGL中间进行通信,需要调用readPixels方法,readPixels方法会发送信息给GPU并等待GPU返回,这个过程往往可能被WebGL的渲染管线阻塞而导致耗时过久,因而降低了渲染的帧率。
  1. 加载性能

在制作素材时,如果用户上传了高分辨且多帧数的帧素材后,可能导致资源包打出来的体积较大,导致在Web端进行渲染时预加载的耗时过高,影响用户体验。
优化方案
  1. WebWorker 独立线程渲染

为了不阻塞,需要将整个渲染流程转移到另一个不影响主线程的线程,因为要用到Worker。Worker是给Web提供多线程运行的一种简单的解决方案,Worker在后台独立执行,不会干扰主界面。
Worker同时也存在一些局限。
首先Worker无法访问DOM元素,无法直接取到video或image标签进行渲染,也无法直接做在屏canvas的渲染。
其次Worker与主线程的数据隔离,与主线程的数据交互都需要做好异步处理。
最后就是Worker的兼容性,例如Safari对Worker的兼容性就并不好,因此在不支持Worker渲染的浏览器还是使用原有的主线程渲染。
基于这几个点,最终完善了使用Worker做独立线程渲染的方案。
  • 输入画面到Worker
Worker不能直接访问DOM元素或者主线程里的数据,就需要主线程在每帧渲染的时候把当前帧手动发送给Worker。
直接传输文本数据给Worker时,是将文本数据复制一份发送到Worker,在数据量大的场景里效率很低,因此不能通过传输画面buffer的方式。
在一番研究之后发现有两个方案可以实现图像在线程之间的传输,一个是Shared Array Buffer(SAB),一个是ImageBitmap。
SAB的兼容性更差,并且需要后台配合返回特性的HTTP头(Cross-Origin-Opener-Policy和Cross-Origin-Embedder-Policy),无形之中抬高了接入成本。而ImageBitmap是纯前端的实现方案,方便传递的同时性能开销也更低,可以通过createImageBitmap方法将常见的HTMLMediaElement转成ImageBitmap。因此最终选定了使用ImageBitmap的数据传递方案。
const bitmap = await createImagebitmap(video)// postMessage可以将bitmap快速传输到Worker中worker.postMessage({command: 'init',image: bitmap,options: this.options,}, [bitmap])
  • Worker里实时渲染
在Worker中的渲染无法直接输出到在屏canvas中,可以将canvas的控制权转移至离屏canvas,Worker输出到OffscreenCanvas上,此时canvas的画面也会同时更新。
const htmlCanvas = document.createElement('canvas');// 移交控制权const offscreen = htmlCanvas.transferControlToOffscreen();const worker = new Worker('my-worker-url.js');// 将OffscreenCanvas发送至worker进行实时渲染worker.postMessage({ canvas: offscreen }, [offscreen]);
OffscreenCanvas与在屏canvas的WebGL渲染接口基本一致,没有事件回调,只能做渲染操作。再次只需对渲染库做一个OffscreenCanvas的兼容判断,并去掉addEventListener的相关逻辑。
完成Worker渲染的改造之后,SDK的大部分执行流程都进入Worker,主线程空闲超过90%。
  1. 图层渲染合并

在《Web端AR美颜特效技术》中有提到,实现美妆的技术基于Mesh网格+shader实现混合模式。这种方案可以完美地对齐photoshop中的混合模式效果,同时也需要单独渲染用户制作时导入的每一个图层,每个图层对应一个Mesh+shader,造成了性能的消耗。
实际上仔细观察发现,美妆的多个图层渲染的Mesh中,其顶点位置都是完全相同的,只是输入的Material,即着色在Mesh上的纹理,以及混合模式有差异。那么其顶点数据首先是可以复用同一份的,然后相同混合模式的不同图层纹理,完全可以通过Material的Shader整合到一个Mesh当中,以达到实际渲染图层的减少。
将混合模式相同的数据整合到一起后,可以动态生成对应的着色器代码:
const layers = [...]const shader = `precision mediump float;varying vec2 vUvs;// 输入整合的多个纹理${layers.map((layer, index) => `uniform sampler2D uSamplerMakeup${index};`).join('\n')}voidmain() { vec4 result = vec4(0.0); ${layers.map((layer, index) => `// 处理颜色复合 ... `).join('\n')} gl_FragColor = result;}`
  1. 资源加载优化

素材打包:在素材制作打包时,对序列帧类的资源统一打成雪碧图。雪碧图不仅可以缩小整个包的体积,还可以在WebGL渲染的时候有效地减少纹理上传的耗时和GPU 缓存刷新的次数。同时在雪碧图打包的时候对序列帧进行适当的质量和尺寸的压缩。
网络传输:前端拉取资源时,开启gzip压缩,HTTP缓存,节约传输过程的耗时。在前端进行纹理缓存和资源缓存,减少远程拉取资源的次数。由于此类应用的资源多为分离的wasm、图片和配置文件,单独加载难以管理,而且容易出现时序问题导致加载出错,因此对资源采用统一打包的方式进行收敛,进行整体加载。对于比较大的资源包,进行合理的拆分打包,进行充分的并行加载。压缩包在前端进行加载和解析,既能降低加载数据的大小和请求的数量,同时更方便管理和后续前端缓存策略的实施。例如一个通用资源包原始大小为17.1M,压缩后为7.8M,再根据资源结构进行拆分,拆分后最大的包只有3M,对拆分后的包进行并行加载,加载耗时缩短了80%以上。
Worker异步加载:资源包下载完成后进入解析阶段,直到最后返回解析完成的结果,这个过程可能阻塞界面的渲染。Worker支持fetch接口,将这个过程迁移到Worker中执行。
前端纹理缓存:资源存储开启了HTTP缓存,可以在初次加载资源的时候快速从浏览器取到缓存资源。然而解决页面停留时,用户重复点击,资源实际上还可能有重复加载与解析的过程,为了避免这类浪费,在初次加载结束之后,将包对应的数据结构和纹理缓存在前端,当重复点击时,直接根据缓存的数据结构读取对应的纹理缓存即可。纹理的缓存同时带来一个问题就是过多的纹理缓存会占用较大的内存,因此引入一个LRU缓存管理的机制,当纹理占用内存较多时,将最久没有使用过的纹理进行销毁。
更多展望
在进行了一系列优化之后,实际上性能还是有更多极致的探索空间,也会在后续的迭代中不断地进行探索和优化。
压缩纹理:当浏览器在加载一张1024*1024的jpeg的图片时,图片本身可能只有100Kb左右,但在gl.texImage2D处理时,要当做位图进行处理,占用内存达到接近4M。WebGL标准提供了压缩纹理的扩展,支持加载压缩纹理。压缩纹理的压缩方式按照WebGL的寻址方式进行优化,可以大大缩小WebGL解析纹理的时间以及内存占用的消耗。
WebGPU:作为下一代的Web图形API,WebGPU直接对native的DX12、Vulkan、Metal进行了封装,实现跨平台的同时允许直接与底层交互而不必首先与浏览器内核,具有更强大的计算能力和更好的性能,能够对AI模型的运算性能带来进一步的提升。
笔者相信随着Web技术的不断发展,AR应用的体验会越来越接近原生体验,被赋予更多的可能性。以上就是在实现视立方·Web美颜特效产品中遇到的一些性能优化的点以及方案,有感兴趣的同学可以直接扫码体验(Web端体验地址:https://demo.webar.qcloud.com/)点击文末「阅读原文」查看相关文档,申请接入,一起探索不一样的Web宇宙。
↓ 扫码体验 ↓
小程序
Web端
腾讯云音视频在音视频领域已有超过21年的技术积累,持续支持国内90%的音视频客户实现云上创新,独家具备 RT-ONE™ 全球网络,在此基础上,构建了业界最完整的 PaaS 产品家族,并通过腾讯云视立方 RT-Cube™ 提供All in One 的终端SDK,助力客户一键获取众多腾讯云音视频能力。腾讯云音视频为全真互联时代,提供坚实的数字化助力

继续阅读
阅读原文