Rendering mit niedriger Latenz und desynchronisiertem Hinweis

Joe Medley
Joe Medley

Unterschiede beim Stift-Rendering

Stiftbasierte Zeichenanwendungen, die für das Web entwickelt wurden, litten lange Zeit unter Latenzproblemen, da eine Webseite Grafikupdates mit dem DOM synchronisieren muss. Bei jeder Zeichenanwendung können Latenzen von mehr als 50 Millisekunden die Hand-Augen-Koordination des Nutzers beeinträchtigen und die Anwendung erschweren.

Der desynchronized-Hinweis für canvas.getContext() ruft einen anderen Codepfad auf, der den üblichen DOM-Aktualisierungsmechanismus umgeht. Stattdessen wird das zugrunde liegende System angewiesen, so viel Rendering wie möglich zu überspringen. In einigen Fällen wird der zugrunde liegende Buffer des Canvas direkt an den Displaycontroller des Bildschirms gesendet. Dadurch wird die Latenz vermieden, die durch die Verwendung der Renderer-Compositor-Warteschlange verursacht würde.

Wie gut ist das Produkt?

Simultanes Rendern von Sintel

Wenn Sie zum Code gelangen möchten, scrollen Sie nach unten. Um die Funktion in Aktion zu sehen, benötigen Sie ein Gerät mit Touchscreen und vorzugsweise einen Eingabestift. (Auch Finger funktionieren.) Falls vorhanden, können Sie die 2D- oder WebGL-Beispiele ausprobieren. Alle anderen können sich diese Demo von Miguel Casas ansehen, einem der Entwickler, die diese Funktion implementiert haben. Öffnen Sie die Demo, drücken Sie auf „Wiedergabe“ und bewegen Sie den Schieberegler dann zufällig und schnell hin und her.

In diesem Beispiel wird ein einminütiger, 21-sekündiger Clip aus dem Kurzfilm Sintel von Durian verwendet, dem offenen Filmprojekt von Blender. In diesem Beispiel wird der Film in einem <video>-Element wiedergegeben, dessen Inhalt gleichzeitig in einem <canvas>-Element gerendert wird. Viele Geräte können dies ohne Tearing tun, bei Geräten mit Front-Buffer-Rendering wie ChromeOS kann es jedoch zu Tearing kommen. (Der Film ist großartig, aber herzzerreißend. Ich war danach eine Stunde lang nicht zu gebrauchen. Ich hab dich gewarnt.)

Hinweis verwenden

Die Verwendung einer niedrigen Latenz bedeutet nicht nur, desynchronized zu canvas.getContext() hinzuzufügen. Ich werde die Probleme einzeln durchgehen.

Canvas erstellen

Bei einer anderen API würde ich zuerst die Funktionserkennung besprechen. Für den Hinweis desynchronized müssen Sie zuerst den Canvas erstellen. Rufen Sie canvas.getContext() auf und übergeben Sie ihm den neuen desynchronized-Hinweis 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() an. Wenn das zurückgegebene Attributobjekt die Property desynchronized hat, testen Sie sie.

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 ein Flimmern auftreten kann, wenn der Code nicht richtig programmiert ist.

Einige Browser, darunter Chrome, löschen WebGL-Canvasse zwischen Frames. Es ist möglich, dass der Displaycontroller den Puffer liest, während er leer ist, was zu einem Flimmern des angezeigten Bildes führt. Legen Sie preserveDrawingBuffer daher auf true fest.

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

Flimmern kann auch auftreten, wenn Sie den Bildschirmkontext in Ihrem eigenen Zeichencode löschen. Wenn Sie den Bildschirm löschen müssen, zeichnen Sie in einen Offscreen-Framebuffer und kopieren Sie ihn dann auf den Bildschirm.

Alpha-Kanäle

Ein halbtransparentes Canvas-Element, bei dem „alpha“ auf „wahr“ gesetzt ist, kann weiterhin desynchronisiert werden, darf aber keine anderen DOM-Elemente darüber haben.

Es kann nur einen geben.

Die Kontextattribute können nach dem ersten Aufruf von canvas.getContext() nicht mehr geändert werden. Das war schon immer so, aber wenn Sie es noch einmal hören, können Sie Ärger vermeiden, falls Sie es nicht wissen oder vergessen haben .

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

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 weiterhin falsch und ein Kontext mit „alpha“ = „true“ wird nie erstellt.

Unterstützte Canvas-Typen

Der erste Parameter, der an getContext() übergeben wird, ist contextType. Wenn Sie mit getContext() bereits vertraut sind, fragen Sie sich wahrscheinlich, ob neben „2d“ noch andere Kontexttypen unterstützt werden. In der folgenden Tabelle sind die Kontexttypen aufgeführt, die desynchronized unterstützen.

contextType Kontexttypobjekt

'2d'

CanvasRenderingContext2D

'webgl'

WebGLRenderingContext

'webgl2'

WebGL2RenderingContext

Fazit

Weitere Beispiele finden Sie in der Demo. Zusätzlich zum bereits beschriebenen Videobeispiel gibt es Beispiele für die Kontexte '2d' und 'webgl'.