Renderowanie z małym opóźnieniem z zdesynchronizowaną wskazówką

Joe Medley
Joe Medley

Różnice w renderowaniu rysika

Aplikacje do rysowania za pomocą rysika stworzone na potrzeby internetu od dawna mają problemy z opóźnieniami, ponieważ strona internetowa musi synchronizować aktualizacje grafiki z DOM. W przypadku każdej aplikacji do rysowania opóźnienia dłuższe niż 50 ms mogą zakłócać koordynację wzrokowo-ruchową użytkownika, przez co aplikacje stają się trudne w użyciu.

Wskazówka desynchronized dla canvas.getContext() wywołuje inną ścieżkę kodu, która omija standardowy mechanizm aktualizacji DOM. Zamiast tego podpowiedź informuje podstawowy system, aby pomijał jak najwięcej operacji kompozytowania, a w niektórych przypadkach bufor podrzędny kanwy jest wysyłany bezpośrednio do kontrolera wyświetlania ekranu. Pozwala to wyeliminować opóźnienia, które mogłyby wystąpić z powodu użycia kolejki kompozytora.

Jak oceniasz ten produkt?

Jednoczesne renderowanie Sintel

Jeśli chcesz przejść do kodu, przewiń w dół. Aby zobaczyć, jak to działa, potrzebujesz urządzenia z ekranem dotykowym i najlepiej rysika. (palce też się nadają). Jeśli masz taką kamerę, wypróbuj przykłady 2D lub WebGL. Pozostali użytkownicy mogą obejrzeć prezentację Miguela Casasa, jednego z inżynierów, którzy wprowadzili tę funkcję. Otwórz wersję demonstracyjną, naciśnij przycisk odtwarzania, a potem przesuwaj suwak losowo i szybko w obu kierunkach.

W tym przykładzie użyto klipu o długości 1 minuta i 21 sekund z krótkiego filmu Sintel autorstwa Duriana, który jest projektem otwartym w ramach projektu Blender. W tym przykładzie film jest odtwarzany w elemencie <video>, którego zawartość jest jednocześnie renderowana do elementu <canvas>. Wiele urządzeń może to zrobić bez rozrywania obrazu, ale urządzenia z renderowaniem w buforze wejściowym, takie jak ChromeOS, mogą mieć problemy z rozrywaniem. (Film jest świetny, ale łamie serce. Po obejrzeniu filmu przez godzinę byłem bezużyteczny. Tylko nie mów później, że nie ostrzegałem.)

Korzystanie z podpowiedzi

Korzystanie z niskiego opóźnienia to coś więcej niż dodanie desynchronized do canvas.getContext(). Omówię po kolei wszystkie problemy.

Tworzenie obszaru roboczego

W przypadku innego interfejsu API najpierw omówię wykrywanie funkcji. W przypadku podpowiedzi desynchronized musisz najpierw utworzyć kartę. Wywołaj funkcję canvas.getContext() i podaj jej nowy podpowiedź desynchronized o wartości true.

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

Wykrywanie cech

Następnie zadzwoń pod numer getContextAttributes(). Jeśli zwrócony obiekt atrybutów ma właściwość desynchronized, przetestuj ją.

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

Unikanie migotania

Jeśli kod nie jest prawidłowo napisany, może to spowodować migotanie.

Niektóre przeglądarki, w tym Chrome, oczyszczają płótna WebGL między klatkami. Sterownik wyświetlacza może odczytać bufor, gdy jest pusty, co powoduje migotanie wyświetlanego obrazu. Aby tego uniknąć, ustaw preserveDrawingBuffer jako true.

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

Migotanie może też wystąpić, gdy w kodze rysunku usuniesz kontekst ekranu. Jeśli musisz wyczyścić ekran, narysuj na offscreenowym framebufferze, a potem skopiuj go na ekran.

Kanały alfa

Prześwitujący element tła, w którym parametr alpha ma wartość true, może być nadal rozsynchronizowany, ale nie może mieć nad sobą żadnych innych elementów DOM.

Może być tylko jeden

Po pierwszym wywołaniu funkcji canvas.getContext() nie możesz zmieniać atrybutów kontekstu. Zawsze tak było, ale powtórzenie tej informacji może oszczędzić Ci rozczarowania, jeśli nie wiesz o tym lub zapomniałeś(-aś).

Załóżmy, że pobieram kontekst i ustawiam parametr alpha na wartość false, a potem gdzieś w kodzie wywołuję funkcję canvas.getContext() po raz drugi, ustawiając parametr alpha na wartość true, jak pokazano poniżej.

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

Nie jest oczywiste, że ctx1ctx2 to ten sam obiekt. Wartość alpha nadal jest ustawiona na fałsz, a kontekst z wartością alpha równą prawda nigdy nie jest tworzony.

Obsługiwane typy odbitek na płótnie

Pierwszy parametr przekazywany do funkcji getContext() to contextType. Jeśli znasz już getContext(), pewnie zastanawiasz się, czy obsługiwane są inne typy kontekstu niż „2D”. Tabela poniżej zawiera typy kontekstów, które obsługują desynchronized.

contextType Obiekt typu kontekstu

'2d'

CanvasRenderingContext2D

'webgl'

WebGLRenderingContext

'webgl2'

WebGL2RenderingContext

Podsumowanie

Jeśli chcesz zobaczyć więcej, zapoznaj się z przykładami. Oprócz przykładu filmu, który został już opisany, znajdziesz przykłady obrazujące zarówno kontekst 2D, jak i WebGL.