v0.1.0 · 8 个平台,一套 Rust 代码

写一次 Rust。 在 8 个平台上直接画到 GPU 上。

GPUX 把 GPUI 的渲染管线与 GPUI-Kit 组件库封成一层薄宿主抽象, 同一份 Rust 视图代码编译成桌面、移动、鸿蒙、Web 与小程序的本地可执行文件。 没有 WebView,没有 JS 侧,也不需要写桥接宏。

开始使用 浏览原生组件

其他安装方式见 快速开始:cargo install gpux-cli、pnpm、bun、yarn。

METAL · VULKAN · DX12 · XCOMPONENT · WEBGPU 拖动旋转
Release 单文件体积
3.8MB
首屏常驻内存 (RSS)
28MB
冷启动首帧
16ms
进程内服务调用
1.2µs

01 · Platforms

8 个宿主,同一套视图

每个平台都直接对接系统图形接口。窗口、VSync、输入法与安全区由对应的 gpux-desktop / gpux-mobile / gpux-web 宿主接管,业务代码不需要写 #[cfg] 分叉。

macOS
Metal · CoreText
桌面
Windows
DirectX 12 · DirectWrite
桌面
Linux
Vulkan · Wayland
桌面
iOS / iPadOS
Metal · UIKit
移动
Android
Vulkan · NDK · JNI
移动
HarmonyOS NEXT
XComponent · EGL
移动
Web
WASM · WebGPU
Web
小程序
WebGL2 · OffscreenCanvas
小程序
真实 API · 编译期类型检查
src/views/monitor.rs — 声明式视图与 GPUI-Kit 组件 Rust · Store<T> + Router<R> + ActiveTheme
use gpux::prelude::*;
use gpux::ui::component::button::{Button, ButtonVariants as _};

impl Render for ClusterMonitor {
    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        let state = self.store.read();
        v_flex().size_full().p_5().gap_4()
            .bg(cx.theme().background).text_color(cx.theme().foreground)
            .child(format!("活跃节点: {} · P99: {:.2}ms", state.nodes, state.p99_ms))
            .child(
                h_flex().gap_2()
                    .child(Button::new("scale-up").primary().label("+ 扩容节点")
                        .on_click(cx.listener(|this, _, _, cx| {
                            this.store.update(|s| s.nodes += 1);
                            cx.notify();
                        })))
                    .child(Button::new("inspect").outline().label("调用 cluster.inspect")
                        .on_click(cx.listener(Self::on_sample_snapshot))),
            )
    }
}
src/services/cluster.rs — 强类型进程内服务桥接 零 #[tauri::command] 宏展开 · 零 JSON ACL 碎片配置
use gpux::prelude::*;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct InspectReq { pub region: String, pub include_vram: bool }

#[derive(Debug, Serialize, Deserialize)]
pub struct ClusterSnapshot { pub healthy_nodes: u32, pub vram_used_mb: f32, pub frame_budget_ms: f32 }

// 1. 普通强类型 Rust 函数即可作为服务,无需过程宏标记
pub fn inspect_cluster(req: InspectReq) -> anyhow::Result<ClusterSnapshot> {
    Ok(ClusterSnapshot { healthy_nodes: 16, vram_used_mb: 24.8, frame_budget_ms: 0.14 })
}

// 2. 在视图中以 1.2µs 延迟直接做类型安全调用
let req = InspectReq { region: "ap-east-1".into(), include_vram: true };
if let Ok(snap) = self.services.call::<InspectReq, ClusterSnapshot>("cluster.inspect", &req) {
    self.store.update(|s| { s.nodes = snap.healthy_nodes; s.p99_ms = snap.frame_budget_ms; });
}
src/shell/adaptive.rs — 响应式断点与移动端安全区适配 自动适配桌面分栏、折叠屏、iOS 灵动岛与鸿蒙手势导航条
use gpux::prelude::*;

impl ClusterMonitor {
    pub fn render_adaptive_shell(&mut self, width: f32, height: f32) -> Div {
        let responsive = ResponsiveContext::from_viewport(width, height)
            .with_safe_area(if width < 600.0 { SafeAreaInsets::ios_notch() } else { SafeAreaInsets::zero() });

        let root = v_flex().size_full()
            .pt(px(responsive.safe_area.top)).pb(px(responsive.safe_area.bottom));

        if responsive.is_mobile() {
            root.px_4().child(self.render_mobile_cards())
                .child(self.render_bottom_tab_bar(self.router.can_go_back()))
        } else {
            root.px_6().child(
                h_flex().size_full().gap_5()
                    .child(self.render_desktop_sidebar())
                    .child(self.render_telemetry_grid(responsive.breakpoint.recommended_columns())),
            )
        }
    }
}
src/lib.rs — 一套 App 定义同时驱动桌面、移动与 WASM macOS · Linux · Windows · iOS · Android · OHOS · Web · 小程序
use gpux::prelude::*;

pub fn build_app() -> gpux::App {
    App::new().title("GPUX Telemetry Studio").size(1120.0, 720.0)
        .bind("cluster.inspect", inspect_cluster)
        .with_state(ClusterState::default())
}

// 1. 桌面端入口 (macOS Metal / Linux Vulkan / Windows DX12)
pub fn run_desktop() -> anyhow::Result<()> {
    build_app().run_with_view(ClusterMonitor::new)
}

// 2. 移动端 C-ABI 导出 (iOS UIKit / Android NativeActivity / 鸿蒙 XComponent)
#[unsafe(no_mangle)]
pub extern "C" fn gpux_mobile_entry() -> i32 {
    build_app().run_mobile().map(|_| 0).unwrap_or(-1)
}

// 3. Web (WASM) 与小程序端入口 (WebGPU / WebGL2)
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen::prelude::wasm_bindgen(start)]
pub fn wasm_main() -> Result<(), wasm_bindgen::JsValue> {
    build_app().run_web_with_view("gpux-canvas", ClusterMonitor::new)
        .map_err(|e| wasm_bindgen::JsValue::from_str(&e.to_string()))
}
实时窗口模拟器
GPUX Telemetry Studio — Metal 0.14ms / 帧
Breakpoint::Expanded · 3 Columns Router: Route::Nodes
Active Nodes
16
P99 Frame
0.14ms
VRAM RSS
24.8MB
✓ ServiceRegistry 就绪:点击上方按钮测试响应式 Store<ClusterState> 与强类型 RPC 调用
无 DOM 树,无 CSS 重排,直接提交 GPU 图元 查看原生组件文档 →

02 · Design

三个决定,其余是细节

GPUX 不去包装一个浏览器,也不在两种语言之间架桥。它只做三件事。

03 · Capabilities

框架自带的部分

开箱可用,不需要再拼装第三个库。

04 · Benchmarks

同一应用的体积与内存

同一份「响应式状态 + 10,000 行虚拟列表 + 1,000 次服务调用」应用,在 Apple M3 Max (macOS 15.3) 与 Linux Wayland/Vulkan 上对比原生自绘、系统 WebView、Skia/JVM 与 Chromium 四类跨端方案。完整 12 款框架对比与测试方法 →

Release 二进制体积 · strip 后产物

原生 GPU / Skia AOT 与系统 WebView、内置 Chromium 的分发体积差异。

  • Slint (Rust) 3.2 MB
  • GPUX (GPUI) 3.8 MB
  • Iced / Makepad 4.6 MB
  • Tauri 2.0 / Dioxus 8.9 MB
  • Wails v2 (Go) 10.4 MB
  • Avalonia (Native AOT) 15.2 MB
  • Flutter (Impeller) 18.4 MB
  • Qt 6 / QML (C++) 22.0 MB
  • React Native (Hermes) 25.0 MB
  • Compose (KMP · JVM) 48.0 MB
  • Electron (Chromium) 85.0 MB

首屏常驻物理内存 RSS · 含全部子进程

Rust/C++ 原生自绘通常在 18–35 MB;系统 WebView 与 JVM/Skia 在 60–115 MB;Electron 超过 200 MB。

  • Slint (Rust) 20 MB
  • GPUX (GPUI) 28 MB
  • Qt 6 / QML (C++) 32 MB
  • Iced / Makepad 34 MB
  • Lynx (Dual-Thread) 42 MB
  • Avalonia (Native AOT) 48 MB
  • Flutter (Impeller) 62 MB
  • React Native (Hermes) 74 MB
  • Wails v2 (WebView) 82 MB
  • Tauri 2.0 / Dioxus 96 MB
  • Compose (KMP · JVM) 115 MB
  • Electron (Chromium) 210 MB

05 · Documentation

文档

从环境搭建到 8 端交付,一步一步写清楚了。

06 · Community

社区

问题、提案和版本公告都在这些地方。