注意: 运行前,需要将 bin/两个dll 拷贝 到 运行目录,或者 exe所在目录
跨平台图形渲染库 - 基于WebGPU接口规范的OpenGL/GLES后端实现
pi_wgpu 是针对WebGPU API规范的轻量级OpenGL/GLES后端实现,主要特性包括:
- 🚀 跨平台支持:Windows/Android/Web三大平台
- ⚡ 性能优化:通过GL状态机优化减少驱动调用次数
- 🛠️ 兼容性:支持WebGL2标准及扩展(DDS/ASTC压缩纹理)
- 📦 模块化设计:与wgpu-rs生态无缝集成
- ✅ 核心功能
- WebGL2标准兼容(规范文档)
- 压缩纹理扩展支持(DDS/ASTC)
- 多线程资源管理
- 🖥️ 平台支持
平台 GL版本 测试状态 Windows OpenGL 3.3+ ✔️ 已验证 Android GLES 3.0+ ✔️ 已验证 Web/小程序 WebGL2 ✔️ 已验证
在Cargo.toml
中添加:
[dependencies]
pi_wgpu = { version = "0.1", features = ["gl"] } # 默认使用pi_wgpu实现
# pi_wgpu = { version = "0.1", features = ["use_wgpu"] } # 使用原生wgpu实现
cargo run --example triangle --features gl
wasm-pack test --chrome --example triangle
# 访问 http://localhost:8000 查看效果
# 如果想修改访问地址, 将地址设置在`WASM_BINDGEN_TEST_ADDRESS`环境变量中, 如$env:WASM_BINDGEN_TEST_ADDRESS="0.0.0.0:8000", 即可通过ip访问
# https://rustwasm.github.io/docs/wasm-bindgen/contributing/testing.html
cargo apk run --example triangle --lib # 打开Linux Shell,在Linux Shell中执行
adb install target/debug/apk/examples/triangle.apk # 在powershell中执行
模块 | 支持状态 | 特性说明 |
---|---|---|
RenderPipeline | ✔️ | 完整渲染管线支持,包含混合/深度测试 |
Texture | ✔️ | 支持ASTC/DDS压缩纹理,自动生成Mipmap |
Buffer | ✔️ | 支持动态数据上传,显存智能管理 |
Shader | ✔️ | GLSL 3.3标准兼容,支持UBO绑定 |
平台 | 特性限制 |
---|---|
Windows | 需要OpenGL 3.3+,不支持Geometry Shader |
Android | 要求GLES 3.0+,部分纹理格式需要扩展支持 |
Web | 依赖WebGL2规范,iOS需≥15.5版本 |
欢迎通过Issue或PR参与贡献!请遵循:
- 提交前运行
cargo fmt
格式化代码 - 新增功能需包含测试用例
- 更新相关文档
本项目采用 MIT License - 详情请查看许可证文件。
- 问题追踪: GitHub Issues
- 开发者文档: API参考手册
- 技术博客: 实现原理分析
- 社区讨论: Discord技术频道
性能优化
:设置状态机,做GL的全状态比较;所以GL指令数量会比wgpu-rs少;
注1:为什么不直接用 wgpu-rs ;
Vulkan
/WebGPU
因兼容性问题,近期内不能广泛使用于各个平台;
注2:根据微信小游戏的开发者文档,在 iPhone / iPad 上,WebGL2
至少要 iOS
版本 >= 15.5 才能开启
注3:详细的 设计笔记,见这里
- 销毁资源:可以在录制指令时 创建 / 销毁 资源
- 线程安全:
- 录制指令 是
单线程
,录制即为提交,所谓的队列提交是空实现; - 创建,销毁 资源,Exe / Apk
多线程
; 注
:使用者自己确保,创建/释放 和 录制指令 的 线程安全性;
- 录制指令 是
- GLSL:
- 功能: 仅支持 gles-300 / std140-布局;
- 语法: 仅支持 GLSL 450 语法;
- 必须等该资源的物体
draw
之后,才能销毁; RenderPass
绑定的 纹理 资源,必须等到RenderPass
Drop 方法调用后,才能销毁;- 原因:因为 不知道调用顺序,vb / ib / ubo 的调用都是先缓存,等到 draw() 内部 才 统一调用的 gl 函数;
例子:
pass.set_vertex_buffer(vb1)
// 不能 调用 vb1 的 drop
pass.set_bind_group(bg1)
// 不能 调用 bg2 的 drop
pass.set_render_pipeline(rp1)
// 不能 调用 rp1 的 drop
pass.draw()
// 这里之后,可以扔掉 vb1, bg1, rp1
pass.set_vertex_buffer(vb2)
函数 | 支持 | 说明 |
---|---|---|
QuerySet | × | WebGL2 本身支持 |
hal::Fence | × | WebGL2 本身支持 |
RenderBundle | × | |
ComputePipeline | × | |
ComputePass | × | |
hal::Barrier | × |
函数 | 支持 | 说明 |
---|---|---|
backend_bits_from_env |
✔ | 永远返回 Some(Backends::GL) |
create_texture_with_data |
✔ | |
create_buffer_init |
✔ |
函数 | 支持 | 说明 |
---|---|---|
new |
✔ | |
request_adapter |
✔ | |
create_surface |
✔ | |
enumerate_adapters | × |
函数 | 支持 | 说明 |
---|---|---|
request_device |
✔ | |
features |
✔ | |
limits |
✔ | |
get_info |
✔ | |
get_downlevel_capabilities |
✔ | |
is_surface_supported |
✔ | |
get_texture_format_features |
✔ |
函数 | 支持 | 说明 |
---|---|---|
configure |
✔ | present_mode : 必须是Fifo ; alpha_mode : 必须是 Opaque |
get_current_texture |
✔ | |
get_default_config | × | |
get_capabilities | × |
函数 | 支持 | 说明 |
---|---|---|
present |
✔ |
注: 所有方法,都要等 Surface.configure(Device, &config) 调用之后,才能使用!
函数 | 支持 | 说明 |
---|---|---|
features |
✔ | |
limits |
✔ | |
create_shader_module |
✔ | |
create_command_encoder |
✔ | |
create_bind_group_layout |
✔ | |
create_bind_group |
✔ | |
create_pipeline_layout |
✔ | |
create_render_pipeline |
✔ | 参数 layout: Option<&'a PipelineLayout> 必须 有值! |
create_buffer |
✔ | |
create_texture |
✔ | |
create_sampler |
✔ | |
create_shader_module_unchecked | × | |
create_shader_module_spirv | × | |
create_render_bundle_encoder | × | |
create_compute_pipeline | × | |
create_texture_from_hal | × | |
create_query_set | × | |
poll | × | |
on_uncaptured_error | × | |
push_error_scope | × | |
pop_error_scope | × | |
start_capture | × | |
stop_capture | × | |
as_hal | × |
注: 所有方法,都要等和该Queue对应的Device 调用 Surface.configure(Device, &config) 之后,才能使用!
函数 | 支持 | 说明 |
---|---|---|
write_buffer |
✔ | |
write_texture |
✔ | |
submit |
✔ | 空实现 |
on_submitted_work_done | × | |
write_buffer_with | × | |
copy_external_image_to_texture | × | |
get_timestamp_period | × |
函数 | 支持 | 说明 |
---|---|---|
finish |
✔ | 空实现 |
begin_render_pass |
✔ | 只支持一个渲染目标 |
clear_texture | × | |
clear_buffer | × | |
begin_compute_pass | × | |
copy_buffer_to_buffer | × | |
copy_buffer_to_texture | × | |
copy_texture_to_buffer | × | |
copy_texture_to_texture | × | |
insert_debug_marker | × | |
push_debug_group | × | |
pop_debug_group | × | |
write_timestamp | × | |
resolve_query_set | × |
函数 | 支持 | 说明 |
---|---|---|
set_bind_group |
✔ | |
set_pipeline |
✔ | |
set_blend_constant |
✔ | |
set_index_buffer |
✔ | |
set_vertex_buffer |
✔ | |
set_scissor_rect |
✔ | |
set_viewport |
✔ | |
set_stencil_reference |
✔ | |
draw |
✔ | 参数 first_instance 必须为 0; base_vertex 必须为 0 |
draw_indexed |
✔ | 参数 first_instance 必须为 0; base_vertex 必须为 0 |
set_push_constants | × | |
insert_debug_marker | × | |
push_debug_group | × | |
pop_debug_group | × | |
draw_indirect | × | |
draw_indexed_indirect | × | |
execute_bundles | × | |
multi_draw_indirect | × | |
multi_draw_indexed_indirect | × | |
multi_draw_indirect_count | × | |
multi_draw_indexed_indirect_count | × | |
write_timestamp | × | |
begin_pipeline_statistics_query | × | |
end_pipeline_statistics_query | × |
注:其生命周期还受到 BindGroup
的影响,见BindGroup
注:其生命周期还受到 BindGroup
的影响,见BindGroup
函数 | 支持 | 说明 |
---|---|---|
create_view |
✔ | |
as_image_copy |
✔ | |
size |
✔ | |
width |
✔ | |
height |
✔ | |
depth_or_array_layers |
✔ | |
mip_level_count |
✔ | |
sample_count |
✔ | |
dimension |
✔ | |
format |
✔ | |
usage |
✔ | |
as_hal | × | |
destroy | × |
注:其生命周期还受到 BindGroup
的影响,见BindGroup
函数 | 支持 | 说明 |
---|---|---|
size |
✔ | |
usage |
✔ | |
as_entire_binding |
✔ | |
as_entire_buffer_binding |
✔ | |
slice |
✔ | |
unmap | × | |
destroy | × |
函数 | 支持 | 说明 |
---|---|---|
map_async | × | |
get_mapped_range | × | |
get_mapped_range_mut | × |
BindGroup
会握住它使用的Buffer
, Texture
, Sampler
对象,使其不会被销毁。
如果想要对应的资源被销毁,必须同时扔掉BindGroup
和对应的资源。
- 仅支持 Naga 编译过后,版本为 glsl 3.0 的 无 define 宏 的 glsl
- 扩展:带上一个字段,用于说明 这个shader原始的 set-binding 和 uniform 的 名字
- 代码 模拟
UBO
:用 uniform 模拟Sampler
:用 texture 函数 模拟VS
/FS
: 不能指定 layout- 难点,需要
可行性测试
:要找一套 WebGL Shader 的 编译转换工具,naga
未必支持;- 将 UBO 转成 Uniform;
- 去掉 所有的 layout,同时导出成 json文件,供 rust 设置;
- rust层,调用 WebGL 的 getXXXLocation 取 Uniform / Attribute 的 location,建立 layout 的 hash-map;
- 难点,需要
- 看 扩展 有没有
- 很多纹理格式: 比如 float 纹理,深度 纹理
VAO
几何实例化
- 多目标渲染 / drawBuffers clearBuffer
- 多重采样
- FeedBack
3.2. 微信小游戏: iOS
的 WebGL
WebGL
: 如果发现 iOS 14 的 帧率提升不上去,试试 在 canvas.getContext 接口 将 antialias: trueWebGL
: iOS 版本 14.0 ~ iOS 15.3, 多个 drawcall 使用不同偏移 来 共享VB
/IB
,性能非常糟糕 !
Instance --> Adatper --> (Device, QUeue)
--(Resumed Event)--> Surface
所有的 Device / Queue 的函数,都要等:Surface.configure(Device, &config) 之后调用。
原因:因为 Web平台 没有 跨 Surface 共享的 Context
可以将 所有的 渲染初始化,放到一个 furture,哪个furture 留到 窗口的 Resumed 事件判断并初始化。
然后,configure 还是要 等 Resized 调用(web没有Resize事件,就再 request_draw 中模拟)