Renderización de latencia baja con la sugerencia desincronizada

Joe Medley
Joe Medley

Diferencias en la renderización de la pluma stylus

Las aplicaciones de dibujo basadas en plumas stylus compiladas para la Web han tenido problemas de latencia durante mucho tiempo, ya que una página web tiene que sincronizar las actualizaciones de gráficos con el DOM. En cualquier aplicación de dibujo, las latencias superiores a 50 milisegundos pueden interferir con la coordinación ojo-mano de un usuario, lo que dificulta el uso de las aplicaciones.

La sugerencia desynchronized para canvas.getContext() invoca una ruta de código diferente que pasa por alto el mecanismo de actualización de DOM habitual. En su lugar, la sugerencia le indica al sistema subyacente que omita la mayor cantidad posible de composicio￳n y, en algunos casos, el búfer subyacente del lienzo se envía directamente al controlador de pantalla de la pantalla. Esto elimina la latencia que se generaría con el uso de la cola del compositor del renderizador.

¿Qué te parece?

Renderización simultánea de Sintel

Si quieres ir al código, desplázate hacia adelante. Para verlo en acción, necesitas un dispositivo con pantalla táctil y, de preferencia, una pluma stylus. (Los dedos también funcionan). Si tienes uno, prueba los ejemplos de 2D o WebGL. Para el resto, consulta esta demo de Miguel Casas, uno de los ingenieros que implementó esta función. Abre la demostración, presiona Reproducir y, luego, mueve el control deslizante de forma aleatoria y rápida.

En este ejemplo, se usa un clip de un minuto y veintiún segundos de la película breve Sintel de Durian, el proyecto de película abierta de Blender. En este ejemplo, la película se reproduce en un elemento <video> cuyo contenido se renderiza de forma simultánea en un elemento <canvas>. Muchos dispositivos pueden hacer esto sin seccionamientos, aunque los dispositivos con renderización de búfer frontal, como ChromeOS, por ejemplo, pueden tener seccionamientos. (La película es genial, pero desgarradora. No pude hacer nada durante una hora después de verlo. Ten en cuenta que esto es una advertencia).

Cómo usar la sugerencia

El uso de latencia baja implica más que agregar desynchronized a canvas.getContext(). Analizaré los problemas uno por uno.

Crea el lienzo

En otra API, primero hablaría de la detección de componentes. Para la sugerencia desynchronized, primero debes crear el lienzo. Llama a canvas.getContext() y pásale la nueva sugerencia desynchronized con un valor de true.

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

Detección de atributos

A continuación, llama a getContextAttributes(). Si el objeto de atributos que se muestra tiene una propiedad desynchronized, pruébala.

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

Evita el parpadeo

Existen dos casos en los que puedes provocar parpadeos si no codificas correctamente.

Algunos navegadores, incluido Chrome, borran los lienzos WebGL entre fotogramas. Es posible que el controlador de pantalla lea el búfer mientras está vacío, lo que hace que la imagen que se dibuja parpadee. Para evitar esto, configura preserveDrawingBuffer como true.

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

El parpadeo también puede ocurrir cuando borras el contexto de la pantalla en tu propio código de dibujo. Si debes borrar, dibuja en un búfer de trama fuera de la pantalla y, luego, cópialo en la pantalla.

Canales alfa

Un elemento de lienzo translúcido, en el que el valor de alfa se establece como verdadero, aún se puede desincronizar, pero no debe tener ningún otro elemento DOM por encima.

Solo puede haber uno

No puedes cambiar los atributos de contexto después de la primera llamada a canvas.getContext(). Esto siempre fue así, pero repetirlo podría ahorrarte algo de frustración si no lo sabías o lo olvidaste .

Por ejemplo, supongamos que obtengo un contexto y especifico alpha como falso. Luego, en algún lugar más adelante en mi código, llamo a canvas.getContext() por segunda vez con alpha establecido en verdadero, como se muestra a continuación.

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,
});

No es obvio que ctx1 y ctx2 sean el mismo objeto. Alpha sigue siendo falso y nunca se crea un contexto con alpha igual a verdadero.

Tipos de lienzo admitidos

El primer parámetro que se pasa a getContext() es contextType. Si ya conoces getContext(), sin duda te preguntarás si se admite algo más que los tipos de contexto "2d". En la siguiente tabla, se muestran los tipos de contexto que admiten desynchronized.

contextType Objeto de tipo de contexto

'2d'

CanvasRenderingContext2D

'webgl'

WebGLRenderingContext

'webgl2'

WebGL2RenderingContext

Conclusión

Si quieres ver más, consulta las muestras. Además del ejemplo de video que ya se describió, hay ejemplos que muestran contextos '2d' y 'webgl'.