Rendu à faible latence avec indice de désynchronisation

Joe Medley
Joe Medley

Différences de rendu du stylet

Les applications de dessin basées sur un stylet conçues pour le Web sont depuis longtemps affectées par des problèmes de latence, car une page Web doit synchroniser les mises à jour graphiques avec le DOM. Dans toute application de dessin, les latences supérieures à 50 millisecondes peuvent interférer avec la coordination œil-main de l'utilisateur, ce qui complique leur utilisation.

L'optimisation desynchronized pour canvas.getContext() appelle un autre chemin de code qui contourne le mécanisme de mise à jour DOM habituel. Au lieu de cela, l'indice indique au système sous-jacent d'ignorer autant de composition que possible. Dans certains cas, le tampon sous-jacent du canevas est envoyé directement au contrôleur d'affichage de l'écran. Cela élimine la latence que pourrait causer l'utilisation de la file d'attente du compositeur du moteur de rendu.

Est-ce un bon produit ?

Affichage simultané de Sintel

Pour accéder au code, faites défiler la page vers l'avant. Pour la voir en action, vous avez besoin d'un appareil avec un écran tactile, et de préférence un stylet. (Les doigts fonctionnent aussi.) Si vous en avez un, essayez les exemples 2d ou webgl. Pour le reste d'entre vous, consultez cette démonstration de Miguel Casas, l'un des ingénieurs qui a implémenté cette fonctionnalité. Ouvrez la démo, appuyez sur le bouton de lecture, puis déplacez le curseur rapidement et de manière aléatoire.

Cet exemple utilise un extrait d'une minute et de 21 secondes du court métrage Sintel de Durian, le projet de film ouvert de Blender. Dans cet exemple, le film est lu dans un élément <video> dont le contenu est affiché simultanément dans un élément <canvas>. De nombreux appareils peuvent effectuer cette opération sans désynchronisation des données, mais ceux qui utilisent le rendu du tampon d'affichage, tels que ChromeOS, peuvent présenter une déchirure. (Le film est génial, mais bouleversant. J'ai été inutile pendant une heure après l'avoir vue. Considérez que vous avez reçu une mise en garde.)

Utiliser l'indice

L'utilisation d'une faible latence ne consiste pas seulement à ajouter desynchronized à canvas.getContext(). Je passerai en revue les problèmes un par un.

Créer la toile

Pour une autre API, je parlerais d'abord de la détection de caractéristiques. Pour l'indice desynchronized, vous devez d'abord créer le canevas. Appelez canvas.getContext() et transmettez-lui la nouvelle suggestion desynchronized avec une valeur de true.

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

Détection de fonctionnalités

Appelez ensuite getContextAttributes(). Si l'objet d'attributs renvoyé possède une propriété desynchronized, testez-le.

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

Éviter le scintillement

Il existe deux cas de figure où vous pouvez provoquer un scintillement si vous ne codez pas correctement.

Certains navigateurs, y compris Chrome, effacent les canevas WebGL entre les cadres. Il est possible que le contrôleur d'affichage lise le tampon alors qu'il est vide, ce qui entraîne le scintillement de l'image. Pour éviter cela, définissez preserveDrawingBuffer sur true.

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

Un scintillement peut également se produire lorsque vous effacez le contexte de l'écran dans votre propre code de dessin. Si vous devez effacer le contenu, dessinez dans un framebuffer hors écran, puis copiez-le à l'écran.

Versions alpha

Un élément de canevas translucide, dont la valeur alpha est définie sur "true", peut toujours être désynchronisé, mais aucun autre élément DOM ne doit être placé au-dessus.

Il ne peut y en avoir qu'un

Vous ne pouvez pas modifier les attributs de contexte après le premier appel à canvas.getContext(). Cela a toujours été vrai, mais répéter cela peut vous éviter une certaine frustration si vous n'en êtes pas conscient ou si vous l'avez oublié .

Par exemple, imaginons que vous obteniez un contexte et que vous spécifiiez "alpha" sur "false", puis, plus tard dans le code, j'appelle canvas.getContext() une deuxième fois avec "alpha" défini sur "true", comme illustré ci-dessous.

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

Il n'est pas évident que ctx1 et ctx2 sont le même objet. Alpha est toujours "false", et un contexte avec alpha égal à "true" n'est jamais créé.

Types de canevas compatibles

Le premier paramètre transmis à getContext() est contextType. Si vous connaissez déjà getContext(), vous vous demandez sans doute si d'autres types de contextes sont pris en charge. Le tableau ci-dessous présente les types de contextes compatibles avec desynchronized.

contextType Objet de type de contexte

'2d'

CanvasRenderingContext2D

'webgl'

WebGLRenderingContext

'webgl2'

WebGL2RenderingContext

Conclusion

Pour en savoir plus, consultez les exemples. En plus de l'exemple vidéo déjà décrit, des exemples illustrant les contextes '2d' et 'webgl' sont disponibles.