TauriでもRustで爆速描画したい
はじめに
皆さんこの記事は読みましたでしょうか?
この記事を書いたのは僕の友達で、レンダリングについて色々調べていたら知見が溜まってきたので記事にしました。課題点
一応ここでも挙げておくと
- Rustで画像を処理したとしてもフロントに送るのが遅い
- wasmはnative Rustよりは遅い (ロマンが足りない)
- +Linuxだとwasmは現状並列化ができない (SharedArrayBufferが有効にできないため)
↓
じゃあRustで直接描画するしかない!!
となるわけですね。
他のライブラリを使えばよいのでは?
彼はRustでUIを作りたくないらしいです。
それにTauriは本当に使い心地がいいですしできることが多いのはいいことです。
実際できるのか
できます
青いやつはRustから描画しています。
Linux
Windows
ソースコードは以下
前準備
説明を分かりやすくするためにいくつか先に説明しておくべきものがあります。
まずはwinit。これはクロスプラットフォームのウィンドウ作成とイベントループのライブラリです。これを使うことで、OSに依存しない形でウィンドウを作成し、イベントを処理することができます。
Tauriの使用しているTAOというライブラリはこのwinitのフォークです。
TauriはWRYというWebViewレンダリングライブラリも使っています。
次にraw-window-handleです。これはウィンドウハンドルを抽象化するためのライブラリです。これを使うことで、異なるプラットフォーム間でウィンドウハンドルを統一的に扱うことができます。
このライブラリのHasDisplayHandle
とHasWindowHandle
というtraitがあればsoftbufferや、wgpuなどのライブラリを使うことで描画が可能になります。
ほとんどのRustのGUIライブラリがこれらrust-windowingのcrateに依存している気がします。
本題
AppHandle
のget_webview_window
というメソッドでWebviewWindow
というものを得ることができます。(App
でもいいです)
このWebviewWindow
はHasDisplayHandle
とHasWindowHandle
を実装しています。 (うれしい)
ですがWindows(とたぶんmacOS)の方はまだ描画できません。src-tauri/tauri.conf.json
を編集してapp->windowsの要素に"transparent": true
を追加しましょう。
それからグローバルなcssで
* {
background-color: transparent !important;
}
などと書いて背景を透明にしてください。
ではcargo add softbuffer
してsoftbufferで描画してみましょう
tauri::Builder::default()
.setup(|app| {
let window = tauri::Manager::get_webview_window(app, "main").unwrap();
let window = std::sync::Arc::new(window);
tauri::async_runtime::spawn(async move {
let context = softbuffer::Context::new(window.clone()).unwrap();
let mut surface = softbuffer::Surface::new(&context, window.clone()).unwrap();
let (width, height) = {
let size = window.inner_size().unwrap();
(size.width, size.height)
};
surface
.resize(
std::num::NonZeroU32::new(width).unwrap(),
std::num::NonZeroU32::new(height).unwrap(),
)
.unwrap();
loop {
let mut buffer = surface.buffer_mut().unwrap();
for index in 0..(width * height) {
let x = index % width;
let r = 0;
let g = 0;
let b = 255 * x / width;
let a = 255;
buffer[index as usize] = b | (g << 8) | (r << 16) | (a << 24);
}
buffer.present().unwrap();
}
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
Windowsの方は上の画像みたいになったと思います。
ですがLinuxの方は透けずに画面いっぱいが青いグラデーションで埋まったのではないでしょうか。
問題点
動作が違う
Linuxではフロントを上書きして描画されていて僕の環境だとウィンドウに触れたりするとチカチカしていてたまにフロントの画面が見えます。
ですが、Windows環境だとそういったこともなく、しかも背景部分のみに描画されています、つまりフロントの下に描画されているのです。
羨しいです。
Waylandなのも関係あるかもしれない
普段ウィンドウマネージャにはHyprlandというWayland compositorを使っているのですが、gnome(xorg)だとチカチカはするものの、背景が透明になるだけでフロントの画面は見えなかったです。
とはいえ、フロントの上に描画されているのであまり影響はありません。
Linuxで綺麗に表示したい
普通にウィンドウを作ってしまうとWebViewが邪魔してきて綺麗に表示できないです。
なのでAppHandle
のcreate_tao_window
というメソッドでTAOだけのウィンドウを作ります。
cargo add tao
でTAOを追加してください。
let window = handle
.create_tao_window(|| {
("display".to_string(), {
tao::window::WindowBuilder::new()
.with_title("New Window")
.with_inner_size(tao::dpi::LogicalSize::new(400, 320))
})
})
.unwrap()
.upgrade()
.unwrap();
windowをこれに置き変えれば良いです。
型がWebviewWindow
とWindow
で少し違うのでinner_size
の戻り値がResultじゃなくなってますがそこだけ修正すればいいと思います。
こうすることでマルチウィンドウにはなってしまいますがLinuxでもRustから描画ができるようになりました。
おわり
本当にここらへんドキュメントもないしやってる人が少なすぎて全体的によくわかりません。
Rust力が足りなくて変なコードとかも書いてるかもしれないです。
ご意見あればどしどしお寄せください。
Discussion
RustのUI周り、とくに描画系ってなかなか安定しないですよね。
僕もDioxusでUI作ったときはtailwind使えてめっちゃ楽!って思ってたらリフレッシュ時のチラつきに悩まされました。