HN 日本語サマリー

← 一覧へ戻る
Web開発

GPUなしでWebGLを実行する

WebGL Without a GPU (microlink.io)

6 pointsby Kikobeats2 コメント

要約

Microlinkは、サーバーにGPUがない状態でWebGLコンテンツのスクリーンショットを高速化するための解決策を共有しています。以前はChromeのデフォルトのソフトウェアレンダラーであるSwiftShaderを使用しており、3Dページのキャプチャに約24秒かかっていましたが、ANGLEをMesa llvmpipeに切り替えることで約6秒に短縮されました。これは、仮想ディスプレイのセットアップとMesaのソースからのビルドを含め、特定のChromeフラグを使用することで実現されました。

全文翻訳

WebGLは今やいたるところにあります。3Dマップ、座席表、製品コンフィギュレーター、シェーダーアートのランディングページなど。それはまた、Microlinkにスクリーンショットを依頼できる最も遅いものでした。一つのChromeフラグがそれを修正しました。 GPUのないノードでMesa llvmpipeを介してレンダリングされた、WebGLページ(three.js)のアニメーションスクリーンショットとしてキャプチャされたもの TL;DR 私たちのサーバーにはGPUがありません。WebGLはどこかでレンダリングする必要があります。Chromeのデフォルトのソフトウェアパス(SwiftShader)では、3Dページあたり約24秒かかりました。ANGLEをMesa llvmpipe(--use-angle=gl)に向けることで、約6秒に短縮されました。一行のフラグは簡単な部分です。ディスプレイ、ソースからのMesa、そしてそれが高速パスにとどまることの証明が残りの話です。 意図的にGPUなし 私たちのブラウザフリートは、グラフィックカードも/dev/driもない汎用Linuxノードで動作しています。安価で、シンプルで、管理するドライバが少ないためです。しかし、WebGLはGPU APIなので、GPUがない場合、何かをCPUでエミュレートする必要があります。24秒のスクリーンショットと6秒のスクリーンショットの違いは、この「何か」でした。 ChromeはWebGLをレンダリングしません。ANGLEがレンダリングします。ChromeはWebGLをANGLEに渡し、ANGLEはそれをプラットフォームが持つバックエンド(Direct3D、Metal、ネイティブOpenGLまたはVulkan、またはGPUがない場合のソフトウェアレンダラー)に変換します。GPUがない場合、そのソフトウェアレンダラーがすべてです。Chromeには2つのソフトウェアレンダラーが付属しています。バンドルされているデフォルトのSwiftShaderと、LinuxノードではMesa llvmpipeであるシステムOpenGLスタックです。CPU上では同じピクセルですが、速度は劇的に異なります。 なぜ4倍の差があるのか SwiftShaderはパイプライン全体を保守的にエミュレートし、「どこでも正しく描画する」ことを最適化します。重い3Dシーンでは約24秒かかり、隣接する2Dページでは2〜3秒かかります。llvmpipeは異なる方法で構築されています。 * ネイティブコードにJITコンパイルします。LLVMはライブシェーダーとGLステートを実際のx86-64にコンパイルします。インタープリターループはありません。 * タイル化され、マルチスレッドです。実際にすべてのコアを使用します。数倍高速で、同じ出力です。 変更点:一行 - '--use-angle=swiftshader', + '--use-angle=gl', 絶対に追加してはいけないもの:`--disable-gpu`は黙ってSwiftShaderを再び強制します。これはすべてのヘッドレスチュートリアルで最もコピーされているフラグです。`--in-process-gpu`はANGLEが必要とするGLサーフェスを殺します。 注意点:ディスプレイが必要です `--use-angle=gl`はGLサーフェスをバインドする必要があり、ヘッドレスであってもXディスプレイが必要です。ディスプレイがない場合、WebGLは黙ってフラットな2Dフォールバックに劣化します。スクリーンショットは成功し、リクエストは200を返しますが、出力は間違っているがもっともらしいものになります。そのため、各コンテナはChromeが起動する前に仮想ディスプレイ(Xvfb)を起動し、`LIBGL_ALWAYS_SOFTWARE=1`でMesaをllvmpipeに固定します。 なぜMesaがソースからビルドされるのか Ubuntu jammyのMesaはこれには古すぎ、以前それをバックポートしていたPPAはなくなりました。そのため、ベースイメージは独自のものをコンパイルします。 `meson setup build \ -Dbuildtype=release -Dgallium-drivers=llvmpipe -Dvulkan-drivers= \ -Dllvm=enabled -Dshared-llvm=enabled` llvmpipeのみ、Vulkanなし、共有LLVM(ここにJIT速度があります)。ビルドツールチェーンは巨大(LLVM、clang、Rust、約160個の-devパッケージ)なので、Dockerfileはマルチステージです。Mesaをコンパイルし、その後にアーティファクトのみをクリーンなイメージにコピーします。4.5GBではなく2.65GBになります。 証明:browserless.report() ノードがどのレンダラーを使用しているかを外から見るだけではわかりません。`apt list`は嘘をつきます(パッケージの上にMesaをサイドロードしているため)、そして実際の答えはページ内にあります。そこで、`browserless.report()`はライブGLコンテキストに直接問い合わせます。 `const browserless = require('browserless') const report = await browserless.report() console.log(report)` 実稼働ノードからの`browserless.report()`。`gpu`と`cpu`を展開して全体像を確認してください。`gpu`ブロックが全体像です。`type`はここでは`software / llvmpipe`です。`swiftshader`はフォールバックしたことを意味し、`hardware`はGPUが出現したことを意味します。`mesa`は、`dpkg`がサイドロードされた古いパッケージバージョンを報告するのではなく、ロードされた`libgallium-<ver>.so`から読み取られます。`simdWidth: 256`は、llvmpipeがAVX2を使用していることを意味し、それが高速である理由のほとんどです。`report({ benchmark: true })`は、ノードを比較するための決定論的なシェーダーベンチマーク(llvmpipeで約300ms)を追加します。 そのレポートはCIゲートでもあります フラット2Dフォールバックは、成功したように見えるため危険です。そのため、CIは`report()`でアサートします。`gpu.type`は`software`でなければならず、`gpu.device`は`llvmpipe`でなければなりません。少しでもずれがあれば、ビルドは失敗し、フラットな3Dが出荷されることはありません。同じ呼び出しが本番ポッドに対しても実行されます。 ほとんどの作業はベンチマークであり、コーディングではありませんでした 差分は一行です。それが正しい行であることを証明するには、数週間の測定が必要でした。そのほとんどは、2つの落とし穴と戦うことでした。 * 開発マシンは嘘をつきます。実際のGPUは、プロダクションで真っ黒になるページをレンダリングします。すべての数字は、プロダクションと同じハードウェアから得られる必要がありました。 * シングルランは嘘をつきます。コールドJIT、ファーストペイントの競合、共有コア。最も速く見える結果が、時には間違っている場合がありました。フラットなフォールバックは、約1秒早く出荷されました。そのため、決定論的なベンチマークが存在します。固定されたシェーダー、強制されたフレーム、安定した数字です。これにより、比較は逸話的なものではなくなりました。SwiftShaderは約24〜31秒、llvmpipeは暖かく正しい状態で約6秒でした。 数字 同じ3Dチャート、同じGPUなしハードウェアで、本番環境で測定されました。 | | SwiftShader (before) | Mesa llvmpipe (after) | | :------------------- | :------------------- | :--------------------- | | Render time (isolated) | ~24s | ~6s (~4×) | | Render time (under load) | ~24s | 7–14s (~2×) | | Failed requests | timed out → errors | none | | Active renderer | SwiftShader | llvmpipe (asserted in CI) | 分離された環境では、チャートは約6秒で完了します。実際のトラフィックでは、キャプチャがコアを共有するため、約2倍の時間がかかると予想されます。いずれにせよ、以前タイムアウトしていたリクエストは完了するようになりました。 以下の例は、CLI、cURL、JavaScript、Python、Ruby、PHP、GolangでMicrolink APIを使用する方法を示しています。`https://threejs.org/examples/webgl_animation_skinning_blending` URLをターゲットとし、APIパラメータ`screenshot`を使用しています。 **CLI Microlink APIの例** `microlink https://threejs.org/examples/webgl_animation_skinning_blending&screenshot.animated` **cURL Microlink APIの例** `curl -G