开源跨端PixUI开始支持WebGPU啦!

8_Metrics

  作者开源的项目PixUI是一个跨端的UI库,底层依赖Skia进行像素级界面绘制。之前使用的Skia版本较旧一直没有更新,这次准备更新至较新的版本,在更新过程中发现Skia的Graphite支持已经可以使用了,所以这次准备先在Web端尝试使用WebGPU来驱动界面绘制。

一、Ganesh vs Graphite

简单来说,Ganesh 是 Skia 传统的、基于状态机的渲染引擎,而 Graphite 是 Skia 下一代的、为现代图形 API(如 Vulkan, Metal,
Dawn)重新设计的渲染引擎。以下是它们在架构、设计理念、目标 API 以及性能特性方面的详细对比:

1. 核心设计哲学

Ganesh:

  • 设计思想:基于“状态机”(State Machine)的概念,深受 OpenGL 的影响。在 Ganesh 中,通过不断改变全局状态(如 glBindTexture,
    glUseProgram)来驱动渲染。
  • 工作方式:它高度依赖于对当前渲染状态的维护和跟踪(State Tracking)。当要画一个图形时,引擎会检查当前状态与目标状态是否一致,如果不一致,则发出状态切换命令。

Graphite:

  • 设计思想:基于“显式状态/描述符”(Explicit State/Descriptors)的概念,深受现代图形 API(Vulkan, Metal, WebGPU)的影响。
  • 工作方式:不再强调“更改状态”,而是强调“提供描述符”。它将渲染逻辑与状态管理解耦,通过构造完整的渲染指令(Render
    Pass/Command
    Buffer)来驱动硬件,更加符合现代 GPU 的工作模式。

2. 对图形 API 的支持

Ganesh:

  • 首选/灵魂 API:OpenGL。虽然它也可以通过抽象层支持 Vulkan 或 Metal,但其内部逻辑(如状态跟踪机制)是为 OpenGL 这种隐含状态驱动的
    API 量身定做的。
  • 痛点:在面对 Vulkan 或 Metal 等需要显式管理资源(如 Pipeline State Objects, PSOs)的 API
    时,Ganesh的性能开销往往来自于繁重的状态同步和兼容层转换。

Graphite:

  • 首选 API:Vulkan, Metal, Dawn (WebGPU)。
  • 优势:Graphite 从底层设计就是为了这些“现代 API”而生的。它原生理解什么是 Pipeline State、什么是 Render Pass、什么是
    Descriptor Set。这使得它在这些 API 上能发挥出极高的效率,减少了 CPU 端转换开销。

3. 状态管理与性能

特性 Ganesh Graphite
状态管理方式 隐式/跟踪式。引擎需要记住当前是什么状态,并在每次绘制前比对。 显式/对象式。状态被封装在不可变的对象(如 Pipeline)中,直接提交。
CPU 开销 由于需要进行大量的状态比对(State Tracking),在复杂的场景下 CPU 开销较大。 通过预先构建 Pipeline 等对象,减少了运行时对状态的判断和维护开销。
多线程支持 较弱。由于全局状态或上下文绑定的限制,很难在多个线程中并行录制渲染命令。 极强。Graphite 的设计目标之一就是支持多线程录制(Multi-threaded Command Recording),这在现代大型应用中至关重要。

4. 架构层级对比

Ganesh 的流程:

SkCanvas → Ganesh Backend (进行繁重的状态跟踪/同步) → Graphics API (如 OpenGL)。

Graphite 的流程:

SkCanvas → Graphite Recording (生成独立的、线程安全的命令序列) → Graphite Execution (将命令转化为现代 API 的资源对象) →
Graphics API (如 Vulkan/Metal)。

二、踩坑记录

1. SkSL转换WGSL非法函数名错误问题

Skia引擎在转换SkSL语言至WebGPU的WGSL时报错非法函数名,发现转换后的函数名如 fn 1DBlurXXX()
是WGSL不支持的,暂手动修改Skia源文件: src/core/SkKnownRuntimeEffects.h如下所示数字打头的名称加任一字符使函数名有效。

#define SK_ALL_STABLEKEYS(M, M1, M2) \
    M2(Invalid, Start)      \
    M1(F1DBlurBase)          \
    M2(F1DBlur4, F1DBlurBase) \
    M(F1DBlur8)              \
    M(F1DBlur12)             \
    M(F1DBlur16)             \
    M(F1DBlur20)             \
    M(F1DBlur28)             \
    M1(F2DBlurBase)          \
    M2(F2DBlur4, F2DBlurBase) \
    M(F2DBlur8)              \
    M(F2DBlur12)             \
    M(F2DBlur16)             \
    M(F2DBlur20)             \
    M(F2DBlur28)             \

2. 画界面卡顿问题

刚开始画界面出现严重卡顿,如下火焰图所示,绘制一帧要几十毫秒时间。光盯着mono_get_frame_info
,一直以为是Skia底层抛出了什么异常,浪费了很多调试时间。最后发现是托管层在包装Skia对象句柄时调用了Enviroment.StackTrace
,去掉即正常了。
8_Flame

三、如何体验

1. clone项目

git clone https://github.com/enjoycode/PixUI.git
git submodule update --init --recursive

2. 修改项目文件 PixUI.Drawing.Skia.csproj和PixUI.Platform.Blazor.csproj


<SkiaUseGraphite>True</SkiaUseGraphite>

如果是False使用原来的Ganesh引擎

3. 启动PixUI.Platform.Blazor项目即可在浏览器内打开Demo

四、本篇小结

  本次更新还未充分利用Graphite的多线程能力,有待后续进一步完善。作者个人能力实在有限Bug在所难免,如有问题请邮件联系或Github Issue,欢迎感兴趣的小伙伴们加入共同完善,当然更欢迎赞助项目或给作者介绍工作(目前找工作中)。

posted @ 2026-09-29 15:34  白菜园  阅读(125)  评论(2)    收藏  举报