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

Joe Medley
Joe Medley

触控笔渲染的差异

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

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

此产品有多好?

同时渲染 Sintel

如果您想查看代码,请向前滚动。如需查看实际应用,您需要带触摸屏的设备,最好是触控笔。(手指也可以)。如果您有 2dwebgl 示例,不妨查看一下。对于其他开发者,请查看实现此功能的工程师 Miguel Casas 提供的演示。打开演示版,按下“播放”,然后随机快速地来回移动滑块。

此示例使用了 Blender 开放式电影项目 Durian 的短片 Sintel 中的片段,时长为 1 分 21 秒。在此示例中,电影在 <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' 上下文的示例。