使用不同步提示进行低延迟渲染

Joe Medley
Joe Medley

触控笔渲染方面的差异

长期以来,为 Web 构建的基于触控笔的绘图应用一直存在延迟问题,因为网页必须将图形更新与 DOM 同步。在任何绘图应用中,延迟时间超过 50 毫秒都可能会干扰用户的手眼协调,使应用难以使用。

canvas.getContext()desynchronized 提示会调用绕过常规 DOM 更新机制的其他代码路径。相反,该提示会告知底层系统尽可能跳过尽可能多的合成,在某些情况下,画布的底层缓冲区会直接发送到屏幕的显示控制器。这样可以消除使用渲染程序合成器队列所造成的延迟。

此产品有多好?

同时渲染 Sintel

如果您想查看代码,请向下滚动。如需查看其运作方式,您需要使用带触摸屏的设备,最好还要使用触控笔。(手指也可以。)如果您有,请尝试使用 2dwebgl 示例。其他用户可以观看由 Miguel Casas 演示此功能的视频,Miguel Casas 是实现此功能的工程师之一。打开演示,按下“播放”按钮,然后快速随机来回移动滑块。

此示例使用了 Blender 开源电影项目 Durian 制作的短片 Sintel 中的一个时长为一分二十一秒的剪辑。在此示例中,电影在 <video> 元素中播放,其内容同时呈现到 <canvas> 元素。许多设备都可以在不撕裂的情况下执行此操作,但具有前端缓冲区渲染的设备(例如 ChromeOS)可能会出现画面撕裂。(这部电影很棒,但令人心碎。 看完后,我整整一个小时都没法动弹。别怪我没警告你。)

使用提示

使用低延迟时间不仅仅是将 desynchronized 添加到 canvas.getContext()。我会逐个解决这些问题。

创建画布

在另一个 API 中,我会先讨论功能检测。对于 desynchronized 提示,您必须先创建画布。调用 canvas.getContext(),并向其传递值为 true 的新 desynchronized 提示。

const canvas = document.querySelector('myCanvas');
const ctx = canvas.getContext('2d', {
  desynchronized: true,
  // Other options. See below.
});

功能检测

接下来,调用 getContextAttributes()。如果返回的属性对象具有 desynchronized 属性,请对其进行测试。

if (ctx.getContextAttributes().desynchronized) {
  console.log('Low latency canvas supported. Yay!');
} else {
  console.log('Low latency canvas not supported. Boo!');
}

避免闪烁

如果代码编写不正确,可能会导致以下两种情况。

某些浏览器(包括 Chrome)会在帧之间清除 WebGL 画布。显示屏控制器可能会在缓冲区为空时读取缓冲区,导致绘制的图片闪烁。为避免这种情况,请将 preserveDrawingBuffer 设置为 true

const canvas = document.querySelector('myCanvas');
const ctx = canvas.getContext('webgl', {
  desynchronized: true,
  preserveDrawingBuffer: true
});

当您在自己的绘制代码中清除屏幕上下文时,也可能会出现闪烁。如果您必须清除,请绘制到屏幕外的帧缓冲区,然后将其复制到屏幕。

Alpha 版

半透明的画布元素(alpha 设置为 true)仍然可以取消同步,但其上方不得有任何其他 DOM 元素。

只能有一个

在首次调用 canvas.getContext() 后,您将无法更改上下文属性。这一点一直以来都是如此,但如果您不知道或忘记了,不妨重复一下。

例如,假设我获取了一个上下文并将 alpha 指定为 false,然后在代码的后续某个位置,我再次调用 canvas.getContext(),并将 alpha 设置为 true,如以下所示。

const canvas = document.querySelector('myCanvas');
const ctx1 = canvas.getContext('2d', {
  alpha: false,
  desynchronized: true,
});

//Some time later, in another corner of code.
const ctx2 = canvas.getContext('2d', {
  alpha: true,
  desynchronized: true,
});

ctx1ctx2 是否为同一对象并不明显。alpha 仍为 false,并且系统绝不会创建 alpha 等于 true 的上下文。

支持的画布类型

传递给 getContext() 的第一个参数是 contextType。如果您已经熟悉 getContext(),那么您肯定会想知道除了“2d”情境类型之外,是否还支持其他类型。下表显示了支持 desynchronized 的上下文类型。

contextType 上下文类型对象

'2d'

CanvasRenderingContext2D

'webgl'

WebGLRenderingContext

'webgl2'

WebGL2RenderingContext

总结

如需查看更多此类内容,请参阅示例。除了前面介绍的视频示例之外,还有一些示例同时展示了 '2d''webgl' 上下文。