Rendering met lage latentie met de gedesynchroniseerde hint

Joe Medley
Joe Medley

Verschillen in stylusweergave

Op stylus gebaseerde tekentoepassingen die voor het web zijn gebouwd, hebben lange tijd last gehad van latentieproblemen omdat een webpagina grafische updates moet synchroniseren met de DOM. In elke tekentoepassing kunnen latenties langer dan 50 milliseconden de hand-oogcoördinatie van een gebruiker verstoren, waardoor toepassingen moeilijk te gebruiken worden.

De desynchronized hint voor canvas.getContext() roept een ander codepad aan dat het gebruikelijke DOM-updatemechanisme omzeilt. In plaats daarvan vertelt de hint het onderliggende systeem om zoveel mogelijk composities over te slaan en in sommige gevallen wordt de onderliggende buffer van het canvas rechtstreeks naar de weergavecontroller van het scherm gestuurd. Dit elimineert de latentie die zou worden veroorzaakt door het gebruik van de compositor-wachtrij van de renderer.

Hoe goed is het?

Gelijktijdige weergave van Sintel

Als je de code wilt zien, scroll dan verder. Om het in actie te zien, heb je een apparaat met een touchscreen nodig, en bij voorkeur een stylus. (Vingers werken ook.) Als je er een hebt, probeer dan de 2d- of webgl -voorbeelden. Bekijk voor de rest deze demo van Miguel Casas , een van de ingenieurs die deze functie heeft geïmplementeerd. Open de demo, druk op play en beweeg de schuifregelaar willekeurig en snel heen en weer.

In dit voorbeeld wordt een fragment van één minuut en eenentwintig seconden gebruikt uit de korte film Sintel van Durian, het open filmproject van Blender. In dit voorbeeld wordt de film afgespeeld in een <video> -element waarvan de inhoud tegelijkertijd wordt weergegeven in een <canvas> -element. Veel apparaten kunnen dit doen zonder scheuren, hoewel apparaten met frontbufferweergave, zoals ChromeOS, mogelijk scheuren vertonen. (De film is geweldig, maar hartverscheurend. Ik was een uur nutteloos nadat ik hem had gezien. Beschouw jezelf als gewaarschuwd.)

De hint gebruiken

Er komt meer kijken bij het gebruik van lage latentie dan het toevoegen van desynchronized aan canvas.getContext() . Ik zal de kwesties één voor één bespreken.

Maak het canvas

Bij een andere API zou ik eerst de functiedetectie bespreken. Voor de desynchronized hint moet u eerst het canvas maken. Roep canvas.getContext() aan en geef de nieuwe desynchronized hint door met de waarde true .

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

Functiedetectie

Roep vervolgens getContextAttributes() aan. Als het geretourneerde attributenobject een desynchronized eigenschap heeft, test deze dan.

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

Flikkering voorkomen

Er zijn twee gevallen waarin u flikkering kunt veroorzaken als u niet correct codeert.

Sommige browsers, waaronder Chrome, wissen WebGL-canvas tussen frames. Het is mogelijk dat de beeldschermcontroller de buffer leest terwijl deze leeg is, waardoor het getekende beeld flikkert. Om dit te voorkomen, stelt u preserveDrawingBuffer in op true .

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

Flikkering kan ook optreden wanneer u de schermcontext in uw eigen tekencode wist. Als u moet wissen, teken dan naar een framebuffer buiten het scherm en kopieer die vervolgens naar het scherm.

Alfakanalen

Een doorschijnend canvaselement, waarbij alpha is ingesteld op true, kan nog steeds worden gedesynchroniseerd, maar er mogen geen andere DOM-elementen boven staan.

Er kan er maar een zijn

U kunt de contextkenmerken niet wijzigen na de eerste aanroep van canvas.getContext() . Dit is altijd zo geweest, maar het herhalen ervan kan u enige frustratie besparen als u het niet weet of het vergeten bent.

Laten we bijvoorbeeld zeggen dat ik een context krijg en alpha specificeer als false, en dat ik ergens later in mijn code canvas.getContext() een tweede keer aanroep, waarbij alpha is ingesteld op true, zoals hieronder weergegeven.

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

Het is niet duidelijk dat ctx1 en ctx2 hetzelfde object zijn. Alfa is nog steeds onwaar en er wordt nooit een context gecreëerd waarbij alfa gelijk is aan waar.

Ondersteunde canvastypen

De eerste parameter die aan getContext() wordt doorgegeven, is contextType . Als u al bekend bent met getContext() vraagt ​​u zich ongetwijfeld af of er iets anders dan '2d'-contexttypen worden ondersteund. De onderstaande tabel toont de contexttypen die desynchronized .

contextType Contexttype-object

'2d'

CanvasRenderingContext2D

'webgl'

WebGLRenderingContext

'webgl2'

WebGL2RenderingContext

Conclusie

Als je meer hiervan wilt zien, bekijk dan de voorbeelden. Naast het reeds beschreven videovoorbeeld zijn er voorbeelden die zowel '2d'- als 'webgl'- contexten tonen.