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

Joe Medley
Joe Medley

Różnice w renderowaniu rysikiem

Stworzone z myślą o internecie aplikacje do rysowania oparte na rysiku mają od dawna problemy z opóźnieniami, ponieważ strona internetowa musi synchronizować aktualizacje grafiki z DOM. W dowolnej aplikacji do rysowania czasy oczekiwania dłuższe niż 50 milisekund mogą zakłócać koordynację ruchową (ręka-oko) użytkownika, utrudniając korzystanie z nich.

Wskazówka desynchronized dla elementu canvas.getContext() wywołuje inną ścieżkę kodu, która pomija zwykły mechanizm aktualizacji DOM. Zamiast tego wskazówka informuje system, że ma pomijać tyle komponowania, ile jest to możliwe. W niektórych przypadkach bazowy bufor obszaru roboczego jest wysyłany bezpośrednio do kontrolera wyświetlania ekranu. Eliminuje to opóźnienia, które byłyby spowodowane użyciem kolejki kompozytora mechanizmu renderowania.

Jak Ci się podoba produkt?

Jednoczesne renderowanie obiektu Sintel

Aby znaleźć kod, przewiń przed siebie. Aby zobaczyć, jak to działa, potrzebne jest urządzenie z ekranem dotykowym, a najlepiej rysikiem. (Dobrze sprawdzają się też palce). Jeśli masz taki kod, wypróbuj przykłady 2d lub webgl. Pozostali użytkownicy powinni obejrzeć prezentację autorstwa Miguela Casasa, jednego z inżynierów, który wdrożył tę funkcję. Otwórz wersję demonstracyjną, naciśnij przycisk odtwarzania, a potem losowo i szybko przesuwaj suwak.

W tym przykładzie korzystamy z jednominutowego i dwudziestosekundowego klipu z krótkiego filmu Sintel autorstwa Duriana – otwartego projektu filmowego Blender. W tym przykładzie film jest odtwarzany w elemencie <video>, którego zawartość jest jednocześnie renderowana w elemencie <canvas>. Wiele urządzeń może to zrobić bez przerywania działania, chociaż urządzenia z renderowaniem z przodu, np. w ChromeOS, mogą powodować rozdarcia. (Film jest świetny, ale wzruszający. Po zobaczeniu tego filmu byłam bezużyteczna przez godzinę. Pamiętaj o ostrzeżeniu).

Korzystanie ze wskazówki

Niskie opóźnienie nie tylko zapewnia wiele korzyści, niż dodawanie desynchronized do aplikacji canvas.getContext(). Będę przyglądać się wszystkim problemom po kolei.

Utwórz obszar roboczy

W przypadku innego interfejsu API najpierw omówię wykrywanie funkcji. W przypadku podpowiedzi desynchronized musisz najpierw utworzyć odbitkę na płótnie. Wywołaj metodę canvas.getContext() i przekaż mu nową wskazówkę desynchronized o wartości true.

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

Wykrywanie funkcji

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

Istnieją 2 przypadki, w których niewłaściwy kod może powodować migotanie.

Niektóre przeglądarki, w tym Chrome, usuwają obszary robocze WebGL między ramkami. Kontroler wyświetlacza może odczytać bufor, gdy jest on pusty, co spowoduje, że obraz będzie migotać. Aby tego uniknąć, ustaw preserveDrawingBuffer na true.

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

Migotanie może też wystąpić po wyczyszczeniu kontekstu z ekranu w kodzie własnego rysunku. Jeśli musisz wyczyścić ekran, narysuj obiekt poza ekranem bufora, a potem skopiuj go na ekran.

Kanały alfa

Przezroczysty element canvas, w którym wartość alfa ma wartość Prawda, nadal może być desynchronizowany, ale nie może mieć nad nim żadnych innych elementów DOM.

Może być tylko jedna

Po pierwszym wywołaniu canvas.getContext() nie można zmienić atrybutów kontekstu. Zawsze tak było, ale powtórzenie go może zaoszczędzić frustracji, jeśli coś nie wiesz lub nawet nie pamiętasz .

Załóżmy na przykład, że otrzymuję kontekst i określam wartość alfa jako „fałsz”, a potem w dalszym miejscu kodu wywołuje metodę canvas.getContext() po raz drugi z wynikiem alfa ustawionym na wartość „true” (prawda), 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 ctx1 i ctx2 to ten sam obiekt. Wersja alfa wciąż jest fałszywa, a kontekst z wartością alfa równą prawda nigdy nie jest tworzony.

Obsługiwane typy odbitek na płótnie

Pierwszym parametrem przekazywanym do metody getContext() jest contextType. Jeśli znasz już getContext(), bez wątpienia zastanawiasz się, czy obsługiwane są inne typy kontekstu niż „2D”. W tabeli poniżej znajdziesz typy kontekstu, które obsługują właściwość desynchronized.

contextType Obiekt typu kontekstu

'2d'

CanvasRenderingContext2D

'webgl'

WebGLRenderingContext

'webgl2'

WebGL2RenderingContext

Podsumowanie

Jeśli chcesz dowiedzieć się więcej, przejrzyj przykłady. Oprócz opisanego już przykładu z filmem mamy przykłady z kontekstem „2d” i „webgl”.