近日,React Native 发布了前瞻式的重大更新,主要围绕 Skia & WebGPU 等场景来布局未来的跨平台渲染场景,主要目的是在**“追求与 Web 的对称性”的同时,提供更强大的客户端渲染支持**。
WebGPU
React Native 本次提到,为了对齐 Web 能力并提供强大的图形处理能力,已经在 React Native 开始引入 WebGPU 支持,其效果将确保与 Web 端的 WebGPU API 完全一致,允许开发者直接复制代码示例的同时,实现与 Web Canvas API 对称的 RN Canvas API。
WebGPU 为 React Native 开发者提供直接访问 GPU 底层能力的途径,从而开启高性能图形渲染的支持,实际上 Android 前段时间在统一 Vlukan 路线也提到过,未来 Java/Kotlin 开发者也许可以通过 Java/Kotlin 下利用 WebGPU 来“直接”体验 Vulkan 场景 ,这个在路径上相似。
WebGPU 作为全新的图形和计算 API,未来将会与 React Native 平台紧密集成,特别是与 Reanimated 的集成,进而实现可以在 UI 线程运行示例,并使用手势处理器进行交互等支持。
此外,由 Software Mansion 提出的 TypeGPU,作为一个类型安全的 WebGPU 支撑,目前是为了解决 WebGPU API 在某些方面(如 uniform byte alignment 和 padding)过于底层的问题,核心是提供一套类型安全的基元,桥接 TypeScript 和 WebGPU,从而允许在 JS 端创建类型化缓冲区并生成匹配的着色器代码,甚至允许 JS 函数在 GPU 上运行。
而针对在需要绘制大量对象(如数千个立方体)的场景下,WebGPU 模式下允许创建场景的“录制”,动画时只需设置 uniform buffers 并通过单次绘制调用渲染场景,从而极大降低 FFI 成本。
此外,WebGPU 还引入了 Compute Shaders ,允许在设备上运行通用 GPU 计算,对应案例有:
compute.toys:一个类似 ShaderToy 的项目,但使用 WebGPU Compute Shaders, Compute Shaders 支持将像素值写入不同缓冲区,影响整个帧的多个像素,而不仅仅是分配给单个线程的像素,而其开源 JS 引擎可直接在 React Native 上运行,并集成了 Reanimated 的动画值和手势
TensorFlow.js:可以直接在 React Native 中使用 React Native WebGPU 后端运行,无需 Canvas,仅使用 Compute Shaders
最后,WebGPU 的引入还可以让 React Native 开发者能够利用 ThreeJS 生态,直接引入已有的 3D 库,这让 React Native 的能力进一步对齐了 Web :
同时还有 React Three Fiber ,例如 SpaceX 的 Aaron Grider 将 Three Fiber 用于 Starlink 应用,并进行了将其 GLSL 代码迁移到 TSL(Three.js Shading Language) 从而在 React Native WebGPU 上运行的实验:
WebGPU 无疑给 React Native 打开了新的大门。
React Native Skia
在很久之前 React Native 就开始尝试在其态系统内引入 Skia ,初始化的目的是弥补 React Native 中图形 API 的重要空白,同时解决 React Native 在处理复杂图形和高性能动画方面存的局限性。
React Native Skia 并非简单地将 Skia 封装,它利用了 JSI (JavaScript Interface) 实现了 JS 与 Skia C++ 对象之间的直接和快速通信,同时 React Native Skia 与 Software Mansion 开发的 Reanimated 相互配合,让动画逻辑能够直接在 UI 线程上运行,保证了 Skia 动画的平滑和高性能渲染。
而现在在 React Native 社区,已经有大量使用 React Native Skia 构建的优秀应用,如 Homsphere, Callie, Daze, Azzapp, Phomo, Bindshoot, Prado, Gecko Med, Runna, B42 等等。
另外,本次提及 Skia 并不是做出了什么重大更新,而是针对稳定性做了重大优化,事实上 React Native 发布到现在就经历了不少调整,比如:
早期的
CanvasView
组件通过 JS 回调在主线程进行绘制,当主线程存在阻塞逻辑时,容易导致掉帧和卡顿;而为了解决主线程阻塞问题,后面引入了图像离屏画布 (Image Offscreen Canvas) 的方案,从而支持在单独的线程中进行渲染,然而这种方法需要为每一帧创建新的画布并通过线程传递图像数据,导致了较高的内存消耗 ;之后又利用SkPicture
可以记录和重放绘制指令,从而在线程间共享渲染结果,进而解决了高内存占用的问题。
而这一次 Skia 主要也是针对性能进行了大幅优化,例如:
性能大幅提升:
iOS 动画时间加快高达 50%,Android 动画时间加快高达近 200%:
Android 和 iOS 首次动画帧时间加快高达近 200%
代码库精简了 13%
稳定性增强:修复了 98% 已报告的 Android 崩溃
而最终的目标,是想通过偿还技术债务,让 React Native Skia 能在三个新平台上可用(macOS, tvOS, 和 Node.js),并为未来迁移到 Skia Graphite(使用 WebGPU 的新 Skia 后端)奠定基础。
Graphite 的核心就是优化运行时着色器编译卡顿问题,虽然还是需要运行时生成着色器,但是它支持应用枚举将要使用的图形特性,从而能够在应用启动时甚至提前 (AOT) 预编译所有必需的着色器 ,目前还是实验性阶段。
另外,针对 Skia 在 React Native 实现了统一的后端模型,解决了之前在 Android (OpenGL) 和 iOS (Metal) 上使用完全不同的后端,导致新功能需要开发两次,代码混乱的问题,同时也即将到来的 WebGPU 迁移提供了前置条件。
在实际场景中,社区的 React Native Skia Video 模块,实现了原生纹理(iOS Metal, Android OpenGL)到 React Native Skia 的直接传输,优化了内存和渲染速度,可以被用于视频帧提取、集成和导出等,生态中还有 React Native Vision Camera 和 React Native Video (v7) 等支持 Skia 的模块。
最后,React Native Skia 还有 Skia DOM 的概念,允许开发者使用熟悉的 React 组件语法来声明式地创建和渲染 Skia 图形元素:
而本次通过将原有的 SkiaDOM 从完全可变的 Paper reconciler 迁移到不可变的 Fabric reconciler ,实现动画时无需处理并发,从而让 Android 动画性能提升近 200%,iOS 和 Android 首次动画帧时间提升近 200%。
最后,在 Skia + WebGPU 的场景下,最终还能实现 2D+3D 的混合模式:
事实上这一步其实也是 Skia 的方向之一,采用 WebGPU 作为多个平台上的统一图形后端,利用 WebGPU (通过其 C++ 实现 Dawn) 将充当一个关键的抽象层,让 Skia Graphite 能够以统一的方式与底层的 Vulkan 或 Metal API 进行交互 ,这也是 Graphite-WebGPU 的方向。
而从这个角度看,Skia 和 WebGPU 无疑很好补充了 React Native 以前在底层能力和图形处理的弱项,当然,这也是一场艰难的持久战,所以如果可以借助 ThreeJS 社区的生态,对于 React Native 来说,既能对其 Web 生态,还能提供丰富案例,无疑是最佳的选择。
那么,你用过 React Native Skia 么?对于 WebGPU 客户端路径你怎么看?