wgpu 与 iOS App 集成相比于 Android 要简单一些。
添加 iOS 构建目标支持
# 添加 iOS 构建目标支持
rustup target add aarch64-apple-ios
# 添加 iOS 模拟器构建目标支持
# Intel CPU Mac
rustup target add x86_64-apple-ios
# M1+ Mac
rustup target add aarch64-apple-ios-sim
由于从 A7 芯片(iPhone 5S,iPad Mini 2) 开始,iPhone iPad 都是 64 位的设备,所以我们不需要 armv7s-apple-ios
、 armv7-apple-ios
这两个构建目标。
iOS 模拟器相比于真机设备的特殊之处
当运行 WebGPU 程序时,模拟器并不会试图完全模拟你正在模拟的 iOS 设备的 GPU。例如,如果选择 iPhone 14 Pro 模拟器,它不会试图模拟 A16 GPU 的能力。相反,模拟器会翻译你的任何调用,并将它们引导到 Mac 主机上的选定 GPU。
苹果为模拟器单独提供了一个设备对象,其功能被限制为苹果 GPU 家族的 Apple2 型号(也就是古早的 A8 芯片),这意味着模拟器往往比实际的 GPU 支持更少的功能或更多的限制。从这篇文档 可以查看到功能限制的详情。
开发调试 GPU 应用,使用真机永远是最好的选择。
定义 FFI
在 iOS/macOS 上,使用 CAMetalLayer
也能创建绘制表面的实例,所以我们无须去实现 raw-window-handle 抽象接口。
先给项目添加上必要的依赖:
[target.'cfg(target_os = "ios")'.dependencies]
libc = "*"
objc = "0.2.7"
然后定义一个 IOSViewObj
结构体:
#[repr(C)]
pub struct IOSViewObj {
// metal_layer 所在的 UIView 容器
// UIView 有一系列方便的函数可供我们在 Rust 端来调用
pub view: *mut Object,
// 指向 iOS 端 CAMetalLayer 的指针
pub metal_layer: *mut c_void,
// 不同的 iOS 设备支持不同的屏幕刷新率,有时我们的 GPU 程序需要用到这类信息
pub maximum_frames: i32,
// 外部函数接口,用于给 iOS 端传递状态码
pub callback_to_swift: extern "C" fn(arg: i32),
}
#[repr(C)]
属性标注 IOSViewObj
的内存布局兼容 C-ABI。
什么是 ABI?
ABI 是⼀个规范,它涵盖以下内容:
· 调⽤约定。⼀个函数的调⽤过程本质就是参数、函数、返回值如何传递。编译器按照调⽤规则去编译,把数据放到相应的堆栈中,函数的调⽤⽅和被调⽤⽅(函数本⾝)都需要遵循这个统⼀的约定。
· 内存布局。主要是⼤⼩和对齐⽅式。
· 处理器指令集。
· ⽬标⽂件和库的⼆进制格式。为什么使用 C-ABI?
不同的操作系统、编程语⾔、每种编程语⾔的不同编译器 实现基本都有⾃⼰规定或者遵循的 ABI 和调⽤规范。⽬前只能通过 FFI 技术遵循 C 语⾔ ABI 才可以做到编程语⾔的相互调⽤。也就是说,C-ABI 是唯⼀通⽤的稳定的标准 ABI。这是由历史原因决定的,C 语⾔伴随着操作系 统⼀路发展⽽来,导致其成为事实上的标准 ABI。
假设我们已经实现好了一个 wgpu 程序叫 WgpuCanvas, 现在来实现两个供 iOS 端调用的、控制 WgpuCanvas 初始化及帧渲染的函数:
#[no_mangle]
pub fn create_wgpu_canvas(ios_obj: IOSViewObj) -> *mut libc::c_void {
let obj = WgpuCanvas::new(AppSurface::new(ios_obj), 0_i32);