Rendering mit niedriger Latenz und desynchronisiertem Hinweis

Joe Medley
Joe Medley

Unterschiede beim Rendern des Eingabestifts

Für das Web entwickelte Schreibanwendungen mit Eingabestift haben seit Langem Latenzprobleme, da eine Webseite Grafikaktualisierungen mit dem DOM synchronisieren muss. In jeder Zeichenanwendung können Latenzen von mehr als 50 Millisekunden die Hand-Augen-Koordination des Nutzers beeinträchtigen und die Nutzung der Anwendungen erschweren.

Der desynchronized-Hinweis für canvas.getContext() ruft einen anderen Codepfad auf, der den üblichen Mechanismus zur DOM-Aktualisierung umgeht. Stattdessen weist der Hinweis das zugrunde liegende System an, so viele Zusammensetzungen wie möglich zu überspringen. In einigen Fällen wird der dem Canvas zugrunde liegende Zwischenspeicher direkt an den Anzeige-Controller des Bildschirms gesendet. Dadurch wird die Latenz beseitigt, die durch die Verwendung der Renderer-Compositor-Warteschlange verursacht würde.

Wie gut ist das?

Gleichzeitiges Rendering von Sintel

Wenn Sie zum Code gehen möchten, scrollen Sie weiter. Um es in Aktion zu sehen, brauchst du ein Gerät mit Touchscreen und idealerweise einen Eingabestift. (Finger funktionieren auch.) Wenn Sie eines haben, versuchen Sie es mit den Beispielen für 2D oder Webgl. Die übrigen Nutzer sehen sich diese Demo von Miguel Casas an, einem der Entwickler, die diese Funktion implementiert haben. Öffnen Sie die Demo, drücken Sie die Wiedergabetaste und bewegen Sie den Schieberegler willkürlich und schnell hin und her.

Dieses Beispiel verwendet einen einminütigen, einundzwanzig Sekunden dauernden Clip aus dem Kurzfilm Sintel von Durian, dem offenen Filmprojekt Blender. In diesem Beispiel wird der Film in einem <video>-Element wiedergegeben, dessen Inhalte gleichzeitig in einem <canvas>-Element gerendert werden. Bei vielen Geräten ist dies ohne Tranings, auch wenn bei Geräten mit Front-Zwischenspeichern wie ChromeOS Fehler auftreten können. (Der Film ist großartig, aber schmerzhaft. Nachdem ich es gesehen habe, war ich eine Stunde lang nutzlos. Sie gelten also gewarnt.)

Den Hinweis verwenden

Eine niedrige Latenz kann mehr genutzt werden, als desynchronized zu canvas.getContext() hinzuzufügen. Ich werde die Probleme einzeln durchgehen.

Canvas erstellen

Bei einer anderen API würde ich zuerst über die Funktionserkennung sprechen. Für den desynchronized-Hinweis müssen Sie zuerst den Canvas erstellen. Rufen Sie canvas.getContext() auf und übergeben Sie den neuen Hinweis desynchronized mit dem Wert true.

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

Funktionserkennung

Rufen Sie als Nächstes getContextAttributes() auf. Wenn das zurückgegebene Attributobjekt das Attribut desynchronized hat, testen Sie es.

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

Flimmern vermeiden

Es gibt zwei Fälle, in denen Sie nicht richtig programmieren können, um ein Flackern zu verursachen.

In einigen Browsern wird das WebGL-Canvas zwischen den Frames in Chrome geleert. Es ist möglich, dass der Anzeige-Controller den Zwischenspeicher liest, solange er leer ist, wodurch das gezeichnete Bild flackert. Um dies zu vermeiden, setzen Sie preserveDrawingBuffer auf true.

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

Es kann auch Flackern auftreten, wenn Sie den Bildschirmkontext in Ihrem eigenen Zeichencode löschen. Wenn Sie den Inhalt löschen müssen, zeichnen Sie in einem Framebuffer außerhalb des Bildschirms und kopieren Sie diesen auf den Bildschirm.

Alphakanäle

Ein durchscheinendes Canvas-Element, bei dem Alpha auf „true“ gesetzt ist, kann zwar desynchronisiert werden, darf jedoch keine anderen DOM-Elemente darüber enthalten.

Es darf nur einen

Nach dem ersten Aufruf von canvas.getContext() können Sie die Kontextattribute nicht mehr ändern. Das war schon immer der Fall, aber eine Wiederholung könnte Ihnen Frustration ersparen, wenn Sie es nicht wissen oder vergessen haben .

Angenommen, ich rufe einen Kontext ab und gebe „Alpha“ als „false“ an. Später in meinem Code rufe ich dann noch einmal canvas.getContext() auf, wobei Alpha auf „true“ gesetzt ist, wie unten gezeigt.

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

Es ist nicht offensichtlich, dass ctx1 und ctx2 dasselbe Objekt sind. Alpha ist immer noch falsch und ein Kontext mit Alpha gleich wahr wird nie erstellt.

Unterstützte Canvastypen

Der erste an getContext() übergebene Parameter ist contextType. Wenn Sie mit getContext() bereits vertraut sind, fragen Sie sich zweifellos, ob andere Kontexttypen als „2d“-Kontexttypen unterstützt werden. Die folgende Tabelle zeigt die Kontexttypen, die desynchronized unterstützen.

contextType Kontexttypobjekt

'2d'

CanvasRenderingContext2D

'webgl'

WebGLRenderingContext

'webgl2'

WebGL2RenderingContext

Fazit

Wenn Sie mehr davon sehen möchten, sehen Sie sich die Beispiele an. Zusätzlich zu dem bereits beschriebenen Videobeispiel gibt es Beispiele, die sowohl den Kontext '2d' als auch 'webgl' zeigen.