Rendering a bassa latenza con il suggerimento non sincronizzato

Joe Medley
Joe Medley

Differenze nel rendering dello stilo

Le applicazioni di disegno basate su stilo create per il web soffrono da tempo di problemi di latenza perché una pagina web deve sincronizzare gli aggiornamenti della grafica con il DOM. In qualsiasi applicazione di disegno, latenze superiori a 50 millisecondi possono interferire con la coordinazione mano-occhio dell'utente, rendendo le applicazioni difficili da usare.

Il suggerimento desynchronized per canvas.getContext() richiama un percorso di codice diverso che ignora il meccanismo di aggiornamento del DOM abituale. Il suggerimento indica invece al sistema sottostante di saltare il maggior numero possibile di compositing e, in alcuni casi, il buffer sottostante del canvas viene inviato direttamente al controller di visualizzazione dello schermo. In questo modo si elimina la latenza causata dalla coda di composizione del renderer.

Qual è la tua valutazione?

Rendering simultaneo di Sintel

Se vuoi visualizzare il codice, scorri più avanti. Per vederla in azione è necessario un dispositivo con touchscreen e preferibilmente uno stilo. (Funziona anche con le dita.) Se ne hai uno, prova gli esempi 2d o webgl. Per gli altri, guardate questa demo di Miguel Casas, uno degli ingegneri che hanno implementato questa funzionalità. Apri la demo, premi Riproduci, quindi sposta il cursore avanti e indietro in modo casuale e veloce.

In questo esempio viene utilizzato un clip di un minuto e di ventuno secondi tratto dal cortometraggio Sintel realizzato da Durian, il progetto open movie di Blender. In questo esempio, il filmato viene riprodotto in un elemento <video> i cui contenuti vengono visualizzati contemporaneamente in un elemento <canvas>. Molti dispositivi possono farlo senza problemi, anche se, ad esempio, i dispositivi con buffer frontale, come ChromeOS, potrebbero presentare dei problemi. (Il film è fantastico, ma straziante. Sono stata inutile per un'ora dopo averla vista. Ti avvisano.)

Utilizzo del suggerimento

Utilizzare una bassa latenza può fare molto di più che aggiungere desynchronized a canvas.getContext(). Analizzerò i problemi uno alla volta.

Crea la tela

In un'altra API, vorrei prima parlare del rilevamento delle funzionalità. Per il suggerimento desynchronized, devi prima creare la tela. Richiama canvas.getContext() e trasmetti il nuovo suggerimento desynchronized con valore true.

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

Rilevamento delle funzionalità

Quindi, chiama il numero getContextAttributes(). Se l'oggetto degli attributi restituito ha una proprietà desynchronized, testalo.

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

Evitare sfarfallio

Esistono due casi in cui puoi causare uno sfarfallio se non codifichi correttamente.

Alcuni browser, tra cui Chrome, cancellano i canvas WebGL tra i frame. Il controller del display può leggere il buffer mentre è vuoto, causando lo sfarfallio dell'immagine. Per evitare che ciò accada, imposta preserveDrawingBuffer su true.

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

Lo sfarfallio può verificarsi anche quando cancelli il contesto dello schermo nel tuo codice di disegno. Se devi cancellare i dati, disegna su un framebuffer fuori schermo e copialo sullo schermo.

Canali alfa

Un elemento canvas traslucido, in cui alfa è impostato su true, può comunque essere dissincronizzato, ma non deve avere altri elementi DOM sopra.

Può esserci un solo

Non puoi modificare gli attributi di contesto dopo la prima chiamata a canvas.getContext(). È sempre stato vero, ma ripeterla potrebbe risparmiare frustrazione se non ne sei a conoscenza o se l'hai dimenticato .

Ad esempio, supponiamo di ottenere un contesto e di specificare alpha come falso, quindi in un secondo momento nel codice chiamo canvas.getContext() una seconda volta con alpha impostato su true, come mostrato di seguito.

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

Non è ovvio che ctx1 e ctx2 siano lo stesso oggetto. La fase alfa è ancora falsa e non viene creato un contesto con alfa uguale a vero.

Tipi di canvas supportati

Il primo parametro passato a getContext() è contextType. Se hai già familiarità con getContext(), senza dubbio ti chiederai se sono supportati altri tipi di contesto diversi dai tipi di contesto "2d". La tabella seguente mostra i tipi di contesto che supportano desynchronized.

contextType Oggetto tipo di contesto

'2d'

CanvasRenderingContext2D

'webgl'

WebGLRenderingContext

'webgl2'

WebGL2RenderingContext

Conclusione

Se vuoi vederne altri, dai un'occhiata agli esempi. Oltre all'esempio di video già descritto, sono disponibili altri esempi che mostrano i contesti '2d' e 'webgl'.