Rendu à faible latence avec indice de désynchronisation

Joe Medley
Joe Medley

Différences entre le rendu du stylet

Les applications de dessin basées sur un stylet conçues pour le Web ont longtemps souffert de 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 rend les applications difficiles à utiliser.

L'indice desynchronized pour canvas.getContext() appelle un chemin de code différent qui contourne le mécanisme de mise à jour DOM habituel. À la place, l'indice indique au système sous-jacent de sauter 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 qui serait causée par l'utilisation de la file d'attente du compositeur du moteur de rendu.

Est-ce un bon produit ?

Rendu simultané de Sintel

Si vous souhaitez accéder au code, faites défiler la page. Pour le voir en action, vous avez besoin d'un appareil avec un écran tactile et de préférence d'un stylet. (Vous pouvez aussi utiliser vos doigts.) Si vous en avez un, essayez les exemples 2D ou WebGL. Pour les autres, regardez cette démo de Miguel Casas, l'un des ingénieurs qui ont implémenté cette fonctionnalité. Ouvrez la démonstration, appuyez sur "Play" (Lecture), puis déplacez le curseur de manière aléatoire et rapide.

Cet exemple utilise un extrait de 1 minute et 21 secondes du court métrage Sintel de Durian, le projet de film ouvert Blender. Dans cet exemple, le film est lu dans un élément <video> dont le contenu est simultanément affiché dans un élément <canvas>. De nombreux appareils peuvent le faire sans déchirure, mais les appareils avec rendu de tampon d'affichage, comme ChromeOS, peuvent présenter des déchirures. (Le film est excellent, mais déchirant. J'ai été inutilisable pendant une heure après l'avoir vu. Ne viens pas te plaindre ensuite.)

Utiliser l'indice

L'utilisation de la faible latence ne se limite pas à ajouter desynchronized à canvas.getContext(). Je vais passer en revue les problèmes un par un.

Créer le canevas

Avec une autre API, je commencerais par la détection de fonctionnalités. Pour l'indice desynchronized, vous devez d'abord créer le canevas. Appelez canvas.getContext() et transmettez-lui la nouvelle indication 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-la.

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 où vous pouvez provoquer un scintillement si vous ne codez pas correctement.

Certains navigateurs, y compris Chrome, effacent les canevas WebGL entre les frames. Il est possible que le contrôleur d'affichage lise la mémoire tampon lorsqu'elle est vide, ce qui provoque le clignotement de l'image dessinée. 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, dessinez sur un framebuffer hors écran, puis copiez-le sur l'écran.

Chaînes alpha

Un élément de canevas translucide, dont la valeur alpha est définie sur "true", peut toujours être désynchronisé, mais il ne doit pas être recouvert d'autres éléments DOM.

Il ne peut y en avoir qu'un seul

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

Par exemple, supposons que j'obtienne un contexte et que je spécifie alpha comme étant faux. Ensuite, plus loin dans mon code, j'appelle canvas.getContext() une deuxième fois avec alpha défini sur "true", comme indiqué 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 soient le même objet. Alpha est toujours faux 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 que "2d" sont acceptés. 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 montrent à la fois les contextes 2D et WebGL.