非同期ヒントを使用した低レイテンシのレンダリング

Joe Medley
Joe Medley

タッチペンのレンダリングの違い

ウェブ用に構築されたタッチペン ベースの描画アプリケーションは、ウェブページがグラフィックの更新を DOM と同期する必要があるため、長い間レイテンシの問題に悩まされてきました。描画アプリでは、レイテンシが 50 ミリ秒を超えると、ユーザーの手と目の連携が妨げられ、アプリの使いづらさにつながる可能性があります。

canvas.getContext()desynchronized ヒントは、通常の DOM 更新メカニズムをバイパスする別のコードパスを呼び出します。代わりに、ヒントは基盤となるシステムに、可能な限りコンポジットをスキップするように指示します。場合によっては、キャンバスの基盤となるバッファが画面のディスプレイ コントローラに直接送信されます。これにより、レンダラ コンポジター キューを使用する場合に発生するレイテンシがなくなります。

どのくらい良かったですか?

Sintel の同時レンダリング

コードを確認するには、下にスクロールしてください。実際に試すには、タッチスクリーン搭載のデバイスと、できればタッチペンが必要です。(指でも操作できます)。デバイスをお持ちの場合は、2d または webgl のサンプルをお試しください。まだ試していない方は、この機能を実装したエンジニアの Miguel Casas によるデモをご覧ください。デモを開き、再生ボタンを押してから、スライダーをランダムにすばやく前後に動かします。

この例では、Blender のオープンムービー プロジェクトである Durian の短編映画 Sintel から 1 分 21 秒のクリップを使用します。この例では、映画は <video> 要素で再生され、そのコンテンツは <canvas> 要素に同時にレンダリングされます。多くのデバイスでは、テアリングなしでこの処理を行うことができますが、ChromeOS などのフロント バッファ レンダリングを使用するデバイスではテアリングが発生することがあります。(この映画は素晴らしいですが、心が痛みます。見た後 1 時間は何もできませんでした。忠告はしたからな。

ヒントの使用

低レイテンシを使用するには、canvas.getContext()desynchronized を追加するだけではありません。問題を 1 つずつ確認いたします。

キャンバスを作成する

別の API では、まず特徴検出について説明します。desynchronized ヒントの場合は、まずキャンバスを作成する必要があります。canvas.getContext() を呼び出して、値が true の新しい desynchronized ヒントを渡します。

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

特徴検出

次に、getContextAttributes() を呼び出します。返された属性オブジェクトに desynchronized プロパティがある場合は、テストします。

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

ちらつきを回避する

コードが正しくないと、次の 2 つの場合にちらつきが発生する可能性があります。

Chrome などの一部のブラウザでは、フレーム間で WebGL キャンバスがクリアされます。ディスプレイ コントローラがバッファが空の状態でバッファを読み取ると、描画される画像がちらつくことがあります。これを回避するには、preserveDrawingBuffertrue に設定します。

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

独自の描画コードで画面コンテキストを消去すると、ちらつきが発生することもあります。消去する必要がある場合は、オフスクリーン フレームバッファに描画してから、その内容を画面にコピーします。

アルファ チャンネル

アルファが true に設定された半透明のキャンバス要素は、引き続き非同期化できますが、その上に他の DOM 要素を配置することはできません。

1 つだけ

canvas.getContext() を最初に呼び出した後にコンテキスト属性を変更することはできません。これは常にそうでしたが、知らない方や忘れている方のために、改めてお伝えします。

たとえば、コンテキストを取得して alpha を false に指定し、コードのどこかで alpha を true に設定して canvas.getContext() を 2 回呼び出すとします。

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

ctx1ctx2 が同じオブジェクトであることは明らかではありません。alpha は引き続き false であり、alpha が true のコンテキストは作成されません。

サポートされているキャンバス タイプ

getContext() に渡される最初のパラメータは contextType です。getContext() に精通している方は、「2d」コンテキスト タイプ以外にもサポートされているコンテキスト タイプがあるかどうか疑問に思っていることでしょう。次の表に、desynchronized をサポートするコンテキスト タイプを示します。

contextType コンテキスト タイプ オブジェクト

'2d'

CanvasRenderingContext2D

'webgl'

WebGLRenderingContext

'webgl2'

WebGL2RenderingContext

まとめ

詳しくは、サンプルをご覧ください。すでに説明した動画の例に加えて、'2d' コンテキストと 'webgl' コンテキストの両方を含む例もあります。