Making the Kiosk Smooth
1Choppy
Part 1 ended with the Chromebook booting straight into a success screen: a glowing title, fireworks, a little screen shake when a shell bursts. It worked, but it stuttered. Watching it, you could count the frames.
The first look at the machine told most of the story. One of Firefox's processes was using about a full CPU core, and the graphics chip, a Mali-G52 with a working open-source driver, was sitting near its lowest clock, 366 MHz out of a possible 1 GHz. The drawing was happening on the CPU, one core at a time.
2Turning the GPU on wasn't enough
Firefox keeps GPU drawing off for graphics drivers it hasn't tested, and this chip's driver is one of them. Six settings in the kiosk's Firefox profile force it on: hardware WebRender, and GPU drawing for the 2D canvas the fireworks use. After a restart, Firefox had the graphics device open and the GPU climbed to its top clock.
It was still choppy. The animation used the canvas's 2D drawing commands: every spark, often a couple of hundred at once, was its own circle, and every letter of the title got a fresh blur for its glow on every frame. Moving that onto the GPU one command at a time didn't remove the cost of issuing all those commands from a single thread.
3Leaving the browser
The next question was whether a browser was the right tool at all. The kiosk's Linux had three alternatives in its package list: LÖVE, a small 2D game engine programmed in Lua; raylib, a C library for games; and mpv, a video player that could loop a pre-rendered clip. I went with LÖVE, which needs no compiler and swaps in for Firefox under the same display server.
The port is under 300 lines of Lua. The difference is in how it draws: all the sparks go to the GPU as one batch, added together where they overlap, and each letter's glow is blurred once at startup and reused. It ran at a steady 60 frames a second, the screen's limit, using about 3% of the CPU.
Two things went wrong on the way. The first version drew at 800 by 600 and got stretched, because the display server hands every new window that size and only resizes it to the full screen a moment later; it now rebuilds itself whenever the window changes size. And it looked flatter than the browser version. The difference was in how the original drew: everything, title included, went into one canvas that fades a little each frame instead of clearing. The title's glow builds up over several frames, and the letters leave a soft smear when they bob. Because Firefox was running slowly, the fade also ran fewer times a second, so the trails lasted longer. Matching that in LÖVE brought the look back.
4Back to the browser, with WebGL
There was a middle path: stay in Firefox, but draw with WebGL instead of the 2D canvas. It's the same idea as the LÖVE version. The page keeps one buffer that fades each frame, sends all the sparks and rocket trails to the GPU as one list of triangles, and renders each letter and its glow into a texture once, using the canvas's own blur so the glow looks the same as before. If a browser has no WebGL, the page falls back to the 2D version.
It also runs at 60 frames a second, with Firefox's busiest process down to about 2% of the CPU. Pressing F on the kiosk's keyboard shows the frame rate and which renderer is running.
One thing looked broken and wasn't: after the switch, the kiosk kept showing the old version for several minutes. The kiosk's small web server sends no caching instructions, so Firefox applied its default freshness rule, which kept serving the saved copy. Everything on the kiosk is local, so its Firefox profile now has caching turned off.
| Frames a second | CPU | GPU clock | |
|---|---|---|---|
| Firefox, 2D canvas | choppy | about one core | 366 MHz |
| Firefox, 2D, GPU forced on | choppy | about one core | 1 GHz |
| LÖVE | 60 | about 3% | near 1 GHz |
| Firefox, WebGL | 60 | about 2% | near 1 GHz |
CPU percentages are shares of all eight cores, read with top on the kiosk. I didn't measure the choppy versions' frame rates; the page had no counter yet.
5Why the web version stays
LÖVE has its advantages: a fraction of Firefox's memory, a faster start, and nothing for a browser update to break. But with WebGL closing the speed gap, the web gives this project more:
- One codebase. The kiosk's page and the widget in part 1 are the same JavaScript; the blog can run the WebGL version too.
- Text and layout. The next pages are dashboards: words, numbers, data fetched live with a saved copy for when the network is down. That's what browsers are built for.
- Development. The kiosk can show the Mac's development server and reload as I save. The LÖVE version means copying files and restarting.
- Any screen. An old tablet or a TV stick with a browser can be the next kiosk without a port.
The LÖVE version stays in the project anyway. One command switches the kiosk between the two, which is useful for anything that's pure animation.
6Next
- Dashboards: the morning pages this was all for.
- Sensors: the Chromebook has no pins for sensors, but a small USB adapter gives it the same plug-in connector as the temperature and air sensors I just ordered for the photo station.
- The 30-second wait at the developer screen, and the USB keyboard helper for the next kiosk, both carried over from part 1.
1カクつく
第1回は、Chromebook が起動するとそのまま成功画面が出るところで終わりました。光るタイトル、花火、花火がはじけるときの小さな画面の揺れ。動いてはいましたが、カクついていました。見ていると、フレームが数えられるほどでした。
マシンを最初に見たとき、ほとんど答えは出ていました。Firefox のプロセスの一つが CPU のコアをほぼ一つまるごと使っていて、グラフィックスチップ(オープンソースのドライバーが動く Mali-G52)は最低に近いクロック、最大1 GHz のうち366 MHz にとどまっていました。描画は CPU で、一度に一つのコアで行われていたのです。
2GPU をオンにするだけでは足りなかった
Firefox は、テストしていないグラフィックスドライバーでは GPU 描画をオフにしています。このチップのドライバーもその一つです。キオスクの Firefox プロファイルに六つの設定を入れて、強制的にオンにしました。ハードウェア WebRender と、花火が使う 2D キャンバスの GPU 描画です。再起動すると、Firefox はグラフィックスデバイスを開き、GPU は最高クロックまで上がりました。
それでもカクついていました。アニメーションはキャンバスの 2D 描画コマンドを使っていました。火花は多いときで一度に二百個ほどあり、その一つひとつが別々の円で、タイトルの文字はどれも、光のために毎フレーム新しくぼかしをかけていました。それを一コマンドずつ GPU に移しても、一つのスレッドから大量のコマンドを出すコストは消えませんでした。
3ブラウザーを離れる
次の問いは、そもそもブラウザーが正しい道具なのかということでした。キオスクの Linux のパッケージ一覧には、三つの選択肢がありました。Lua で書く小さな 2D ゲームエンジンの LÖVE、ゲーム用の C ライブラリーの raylib、そして事前にレンダリングした映像をループ再生できる動画プレーヤーの mpv。私は LÖVE を選びました。コンパイラーがいらず、同じディスプレイサーバーの上で Firefox と入れ替えられるからです。
移植は Lua で300行足らずです。違いは描き方にあります。火花はすべて一つのまとまりとして GPU に送られ、重なったところは足し合わされます。各文字の光は起動時に一度だけぼかして、使い回します。画面の上限である毎秒60フレームで安定して動き、CPU の使用率は約3%でした。
途中で二つのことがうまくいきませんでした。最初のバージョンは800×600で描画されて、引き伸ばされていました。ディスプレイサーバーは新しいウィンドウをまずそのサイズで渡し、少しあとで全画面にリサイズするからです。今は、ウィンドウのサイズが変わるたびに作り直すようにしています。もう一つは、ブラウザー版より平板に見えたことです。違いは元の描き方にありました。タイトルも含めてすべてが一枚のキャンバスに描かれ、そのキャンバスは毎フレーム消去されるのではなく、少しずつ薄れていきます。タイトルの光は何フレームもかけて積み重なり、文字が上下に揺れるとやわらかい残像が残ります。Firefox の動きが遅かったので、薄れる処理も一秒あたりの回数が少なく、そのぶん軌跡が長く残っていました。LÖVE でそれを再現すると、見た目が戻りました。
4WebGL で、ブラウザーに戻る
中間の道もありました。Firefox にとどまったまま、2D キャンバスではなく WebGL で描くのです。考え方は LÖVE 版と同じです。ページは毎フレーム薄れていくバッファーを一つ持ち、火花とロケットの軌跡をすべて一つの三角形のリストとして GPU に送り、各文字とその光を一度だけテクスチャーに描きます。光にはキャンバス自体のぼかしを使うので、前と同じ見た目になります。WebGL のないブラウザーでは、ページは 2D 版に切り替わります。
こちらも毎秒60フレームで動き、Firefox の一番忙しいプロセスの CPU 使用率は約2%まで下がりました。キオスクのキーボードで F を押すと、フレームレートと、どのレンダラーが動いているかが表示されます。
壊れているように見えて、そうではなかったことが一つあります。切り替えたあと、キオスクは数分間、古いバージョンを表示し続けました。キオスクの小さな Web サーバーはキャッシュの指示を何も送っていないので、Firefox は既定の鮮度ルールを使い、保存したコピーを出し続けていたのです。キオスクの中身はすべてローカルなので、今は Firefox プロファイルでキャッシュをオフにしています。
| 毎秒フレーム数 | CPU | GPU クロック | |
|---|---|---|---|
| Firefox、2D キャンバス | カクつく | コア約1つ分 | 366 MHz |
| Firefox、2D、GPU 強制オン | カクつく | コア約1つ分 | 1 GHz |
| LÖVE | 60 | 約3% | 1 GHz 近く |
| Firefox、WebGL | 60 | 約2% | 1 GHz 近く |
CPU の割合は8コア全体に対するもので、キオスク上で top を使って読み取りました。カクつくバージョンのフレームレートは測っていません。そのときのページには、まだカウンターがありませんでした。
5Web 版を残す理由
LÖVE にも利点があります。メモリは Firefox のほんの一部で、起動が速く、ブラウザーの更新で壊れるものもありません。でも WebGL で速さの差が埋まった今、このプロジェクトには Web の方が多くのものをくれます。
- コードベースが一つ。キオスクのページと第1回のウィジェットは同じ JavaScript です。ブログでも WebGL 版を動かせます。
- 文字とレイアウト。次のページはダッシュボードです。言葉、数字、ライブで取得するデータ、そしてネットワークが落ちたときのための保存コピー。これこそブラウザーが得意とするものです。
- 開発。キオスクは Mac の開発サーバーを表示して、私が保存するたびに再読み込みできます。LÖVE 版だと、ファイルをコピーして再起動することになります。
- どんな画面でも。ブラウザーのある古いタブレットやテレビ用スティックなら、移植なしで次のキオスクになれます。
それでも、LÖVE 版はプロジェクトに残します。コマンド一つでキオスクを二つの間で切り替えられるので、純粋なアニメーションだけのものには役に立ちます。
6次に
- ダッシュボード:そもそもこれはすべて、朝のページのためでした。
- センサー:Chromebook にはセンサー用のピンがありませんが、小さな USB アダプターを使えば、フォトステーション用に注文したばかりの温度センサーや空気センサーと同じ差し込み式のコネクターが使えます。
- 開発者画面での30秒の待ち時間と、次のキオスク用の USB キーボード補助ツール。どちらも第1回からの持ち越しです。
Want the next one?
New posts by email, now and then. Nothing else.