1. GPUIとGPUI-Componentの概要
デスクトップGUI開発の現状と課題
現代のデスクトップアプリケーション開発では、「開発のしやすさ」と「実行時の軽快さ」をどのように両立させるかが長年の課題となっています。
これまで多くのデスクトップアプリ(VS Code、Slack、Discord など)では、Web ブラウザのエンジン(Chromium)を丸ごと同梱する「Electron」が広く使われてきました。 Web の技術(HTML、CSS、TypeScript)をそのまま使えるため開発効率は非常に高いのですが、アプリを 1 つ起動するだけで数百メガバイトのメモリを消費し、キー入力やスクロールにわずかな遅延(ラグ)が生じるという弱点がありました。
この問題を解決するため、OS 標準の WebView を使う「Tauri」や、Rust 言語だけで画面を描画する GUI フレームワーク(iced、egui、Slint、Dioxus など)が登場しました。 その中でも、「Web フロントエンドのような書きやすさ」と「3D ゲームのような 120 FPS の圧倒的な描画性能」を両立させた次世代のフレームワークとして注目を集めているのが GPUI です。
Zedから誕生した「GPUI」とは
GPUI(GPU-accelerated UI)は、超高速な次世代コードエディタ「Zed」を開発するために、Zed Industries 社(Atom や Electron、Tree-sitter の生みの親である Nathan Sobo 氏らが設立)がゼロから開発した Rust 製の UI フレームワークです。
従来の GUI ツールキットの多くは、OS が用意したボタンやテキストボックスを組み合わせたり、CPU で少しずつ画面を塗り直したりしていました。 それに対して GPUI は、ビデオゲームと同じように 画面上の文字や四角形、影、アイコンのすべてを GPU(グラフィックス処理装置)のシェーダーを使って一括で直接描画 します。
graph LR
subgraph 従来のWebベースUI
JS["JavaScript / DOM"] --> Layout1["ブラウザエンジン計算"]
Layout1 --> Comp["合成・描画(重い・メモリ大)"]
end
subgraph GPUIのアプローチ
Rust["Rust View / State"] --> Taffy["Taffy レイアウト計算"]
Taffy --> GPU["GPU 直接描画(Metal / DirectX / Vulkan)<br/>120 FPS・省メモリ"]
end
GPUI は OS ごとに最適なグラフィックス API(macOS では Metal、Windows では DirectX、Linux では Vulkan や Blade)を直接叩きます。 そのため、数万行のテキストファイルを高速にスクロールしたり、画面を何分割しても、フレームレートが落ちることなく瞬時に反応します。
ハイブリッド描画モードの特徴
GUI フレームワークの設計には、大きく分けて「Retained Mode(保持モード)」と「Immediate Mode(即時モード)」の 2 種類があります。
▸ Retained Mode(保持モード)
HTML の DOM や従来のデスクトップ UI のように、画面の部品(オブジェクト)をメモリ上にずっと保持し続ける方式です。 状態が変わったときに「どの部品のどの文字を書き換えるか」を管理しやすく、複雑なレイアウトに向いていますが、状態と画面の同期コードが複雑になりやすい欠点があります。
▸ Immediate Mode(即時モード)
ゲームのデバッグ画面(Dear ImGui や Rust の egui)のように、画面の部品を保持せず、毎フレームごとに最初からすべての画面を描き直す方式です。 コードがシンプルになる反面、複雑なレイアウト計算や OS のアクセシビリティ連携、フォーカス管理が難しくなる傾向があります。
GPUI は、この 2 つの良いとこ取りをした ハイブリッド描画アーキテクチャ を採用しています。
- 状態とイベントは Retained(保持): アプリケーションのデータ(
Entity)やフォーカス状態は GPUI がメモリ上でしっかりと保持・管理します。 - 見た目の構築は Immediate(宣言的): 状態が変化して再描画が必要になると、React や SwiftUI のように
renderメソッドが呼ばれ、その時点のデータから新しい画面要素のツリーを宣言的に組み立てます。
この設計により、開発者は「データがどう変わったら、どの画面部品をどう書き換えるか」という面倒な手順を考える必要がありません。 「いまのデータなら、こういう画面になるはずだ」という宣言を書くだけで、裏側の GPU パイプラインが高速に描画を完了させます。
なぜ「GPUI-Component」が必要なのか
GPUI は非常に強力なフレームワークですが、もともとは「Zed エディタを作るための専用エンジン」として生まれました。
そのため、単体の gpui クレート(ライブラリ)には、四角形(div)やテキスト、画像、スクロールといった 最も基礎的な部品(プリミティブ)しか入っていません。
いざ業務用のデスクトップアプリや管理ツールを作ろうとしても、一般的なボタン、日本語入力に対応したテキストボックス、ドロップダウンメニュー、カレンダー、大規模なデータテーブル、タブ分割可能なドック画面などをすべて一から自作するのは大変な労力がかかります。
そこで登場したのが、金融テクノロジー企業の Longbridge 社が自社のデスクトップ取引ツール(Longbridge Pro)の開発を通じて構築し、オープンソースとして公開した GPUI-Component(プロジェクト総称 GPUI Kit)です。
| 比較項目 | GPUI(単体) | GPUI + GPUI-Component |
|---|---|---|
| 開発元 | Zed Industries | Longbridge |
| 主な役割 | GPU 描画エンジン・状態管理・イベント基盤 | 実用的な UI ウィジェット群・テーマ管理 |
| 提供される部品 | div、テキスト、SVG、キャンバス等の基礎要素 | 70 種類以上の完成された UI コンポーネント |
| テキスト入力 | 自前でキー入力や IME 制御を書く必要がある | IME・コピペ・Undo 対応の Input をすぐ使える |
| 大量データ表示 | 低レベルな一様リストのみ | 仮想化された高機能な Table や List を完備 |
| 画面分割 | なし(自作が必要) | ドラッグ&ドロップ対応の DockArea を完備 |
GPUI を「高性能なエンジンの車台」とするなら、GPUI-Component は「快適なシート、ハンドル、エアコン、ナビが一式揃った内装キット」にあたります。 この 2 つを組み合わせることで、Rust でありながら Web フロントエンド(React + shadcn/ui や Tailwind CSS)と同じくらい素早く、かつネイティブならではの圧倒的な速度を持つデスクトップアプリを作ることができます。
開発環境の準備と最小プロジェクトの作成
それでは、手元のコンピュータで GPUI と GPUI-Component を使った最小限のアプリケーションを動かす準備をしましょう。
▸ OS ごとの事前準備
Rust のコンパイラ(rustup)がインストールされていることを前提とします。
GPUI は OS のグラフィックス機能やウィンドウ機能を直接利用するため、OS ごとに以下の開発ツールが必要です。
- Windows: Visual Studio C++ Build Tools および Windows 10/11 SDK が必要です。
- macOS: Xcode Command Line Tools(
xcode-select --install)が必要です。 - Linux: Vulkan ドライバや Wayland / X11 の開発用ヘッダライブラリ(
libxcb、libxkbcommon、libwayland-devなど)が必要です。
▸ Cargo.toml の設定
まず cargo new hello-gpui で新しいプロジェクトを作成し、Cargo.toml に gpui と gpui-component を追加します。
[package]
name = "hello-gpui"
version = "0.1.0"
edition = "2021"
[dependencies]
gpui = "0.2"
gpui-component = "0.5"
▸ 最初のウィンドウを表示するコード(src/main.rs)
次に、src/main.rs を以下のように記述します。
これは GPUI-Component を使って画面中央にメッセージとボタンを 1 つ表示する最小のプログラムです。
use gpui::*;
use gpui_component::{
button::{Button, ButtonVariants as _},
v_flex, ActiveTheme as _, Root,
};
// 1. 画面を表す構造体を定義する
pub struct HelloView;
// 2. Renderトレイトを実装して見た目を組み立てる
impl Render for HelloView {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
v_flex()
.size_full()
.items_center()
.justify_center()
.gap_4()
.bg(cx.theme().background)
.text_color(cx.theme().foreground)
.child("Hello, GPUI & GPUI-Component!")
.child(
Button::new("greet-btn")
.primary()
.label("クリックしてください")
.on_click(|_event, _window, _cx| {
println!("ボタンが押されました!");
}),
)
}
}
// 3. アプリケーションを起動してウィンドウを開く
fn main() {
let app = Application::new();
app.run(move |cx: &mut App| {
// GPUI-Componentのテーマやグローバル設定を初期化する
gpui_component::init(cx);
// ウィンドウを開く
cx.open_window(WindowOptions::default(), |window, cx| {
// 自分たちのビュー(HelloView)を作成する
let view = cx.new(|_cx| HelloView);
// GPUI-ComponentのRootで包んでウィンドウのルートビューにする
cx.new(|cx| Root::new(view.into(), window, cx))
})
.expect("ウィンドウの起動に失敗しました");
});
}
cargo run を実行すると、ネイティブウィンドウが立ち上がり、テーマに沿った美しいボタンが表示されます。
次章では、このコードの中に登場した App、Context、cx.new、Render といった GPUI の最も重要な「コアアーキテクチャと状態管理の仕組み」を詳しく解き明かしていきます。