#bind-group #wgsl #wgpu #shader #bindings #bindings-generator #type-safe

wgsl_to_wgpu

从 WGSL 着色器生成类型安全的 Rust 绑定到 wgpu

12 个版本 (破坏性)

0.9.0 2024年7月27日
0.8.0 2024年5月9日
0.6.0 2024年2月23日
0.5.0 2023年10月28日
0.1.0 2021年7月11日

#54 in 图形API

Download history 106/week @ 2024-05-04 31/week @ 2024-05-11 26/week @ 2024-05-18 5/week @ 2024-05-25 2/week @ 2024-06-01 18/week @ 2024-06-08 7/week @ 2024-06-15 13/week @ 2024-06-22 139/week @ 2024-06-29 5/week @ 2024-07-06 6/week @ 2024-07-13 4/week @ 2024-07-20 161/week @ 2024-07-27 2/week @ 2024-08-03 37/week @ 2024-08-10 20/week @ 2024-08-17

每月220次下载
用于 xc3_wgpu

MIT 许可证

190KB
4K SLoC

wgsl_to_wgpu

Latest Version docs.rs
wgsl_to_wgpu 是一个库,可以从 WGSL 着色器生成到 wgpu 的类型安全 Rust 绑定。

wgsl_to_wgpu 设计为通过构建脚本集成到编译过程中。使用 naga 解析 WGSL 着色器以生成相应的 Rust 模块。生成的 Rust 模块包含与 WGSL 着色器模块一起工作所需类型定义和样板代码。使用生成的代码还可以减少许多无效API使用的实例。

wgsl_to_wgpu 促进了一个以着色器为中心的工作流程,其中对 WGSL 代码的编辑将自动反映在相应的 Rust 文件中。例如,改变 WGSL 中统一变量的类型将在使用生成的结构体初始化缓冲区时在 Rust 代码中引发编译错误。

功能

  • 更强的类型初始化 绑定组和绑定
  • 着色器模块初始化
  • 用于顶点、存储和统一缓冲区的 Rust 结构体
  • 用于着色器常量和管道参数(如工作组大小)的 Rust 常量
  • 可选的 derive 为 encase、bytemuck 和 serde
  • 使用 bytemuck 时,对生成的结构体的 WGSL 内存布局 进行 const 验证
  • 可覆盖管道的常量
  • 推常量

用法

将以下行添加到 Cargo.toml 并填写适当的版本号,对于 wgsl_to_wgpu。当启用类似 bytemuck、serde 或 encase 的 crates 的 derives 时,这些依赖项也应添加到 Cargo.toml 中,并带有适当的 derive 功能。请参阅提供的 示例项目 了解基本用法。

[build-dependencies]
wgsl_to_wgpu = "..."

请参阅示例 crate 了解如何使用生成的代码。使用 cargo run 运行示例。

内存布局

WGSL 结构体与 Rust 结构体或标准布局算法(如 repr(C)repr(packed))有不同的内存布局要求。匹配预期布局以在 CPU 和 GPU 之间共享数据可能会很繁琐且容易出错。wgsl_to_wgpu 提供了添加对 encase 的 derives 的选项,以在运行时处理填充和对齐,或对 bytemuck 进行编译时填充和对齐的强制。

在由temuck派生时,wgsl_to_wgpu 将使用 naga 的布局计算来添加 const 断言,以确保所有主机可共享类型(用于统一和存储缓冲区的结构体)的字段都具有WGSL期望的正确偏移量、大小和对齐。强烈建议使用 vec4 或 mat4 等类型而不是 vec3 或 mat3 与 bytemuck 一起使用,以避免对齐不匹配。仅用作顶点输入结构体的结构体使用 std::mem::offset_of 手动指定其布局,并且不会生成布局验证断言。

绑定组

wgpu 使用组织到绑定组中的资源绑定来定义全局着色器资源,如纹理和缓冲区。着色器可以有许多资源绑定,这些绑定组织成最多 4 个绑定组。wgsl_to_wgpu 将以更类型安全的方式生成初始化和设置这些绑定组的类型和函数。在 WGSL 着色器中添加、删除或更改绑定组通常会导致在未更新创建或使用这些绑定组的代码时编译错误,而不是运行时错误。

虽然可以使用 bind_groups::set_bind_groups 函数一次性设置所有绑定组,但建议根据它们的更新频率组织绑定到绑定组中。绑定组 0 的更改频率最低,如每帧资源,而绑定组 3 的更改频率最高,如每绘制资源。可以使用它们的 set(render_pass) 方法单独设置绑定组。这可以为具有许多绘制调用的场景提供轻微的性能改进。请参阅 描述符表频率(DX12)描述符集频率(Vulkan) 了解详细信息。

以这种方式组织绑定组也可以帮助更好地在应用程序代码中组织渲染资源,而不是在每个对象中冗余地存储所有资源。可能只需要存储一次的bindgroups::BindGroup0,而bindgroups::BindGroup3可能需要在场景中的每个网格中存储。请注意,绑定组存储对其底层资源绑定的引用,因此如果只有统一或存储缓冲区的内容发生变化,则不需要重新创建绑定组。如果可能,避免在渲染过程中创建新的绑定组以获得最佳性能。

限制

  • 可能需要禁用对不支持类型或特性的着色器运行此函数。如果任何新的或现有的WGSL语法不受支持,请提交问题。
  • 这个库不是一个渲染库,不会生成任何高级抽象,如材质或场景图。目标是生成使用wgpu与WGSL着色器相关的几乎所有繁琐且容易出错的样板代码。
  • 生成的代码不会防止意外调用来自无关生成模块的函数。建议使用与着色器相同的名称命名着色器模块,并使用唯一的着色器名称以避免问题。在某些情况下,如使用相同的相机结构定义在多个WGSL着色器中,可能希望使用来自不同着色器模块的生成代码。
  • 当前实现假设所有着色器阶段都是单个WGSL源文件的一部分。在未来的版本中可能会支持跨文件的着色器模块。
  • 可以使用错误的生成Rust结构体初始化统一和存储缓冲区。WGPU将在运行时仍然验证缓冲区绑定的尺寸。
  • 目前支持大多数但不是所有的WGSL类型。
  • 在WGSL中使用浮点类型(如vec2<f32>)的顶点属性假定使用浮点输入而不是归一化属性(如unorm或snorm整数)。
  • 所有纹理都假定是可过滤的,所有采样器都假定是过滤的。这可能会导致兼容性问题。通常可以通过请求仅原生支持的TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES功能来解决。
  • 在某些情况下,如避免冗余绑定组绑定或调整资源着色器阶段可见性,可以实现比生成代码略好的性能。这应该通过在某些适当的情况下使用一些手写的代码来解决。
  • 为了简单起见,管线仅定义了一个所有阶段共享的单个推送常量范围。使用单个调用设置数据,偏移量为0,以及适当使用阶段,如wgpu::ShaderStages::VERTEX_FRAGMENT

发布Crate

Rust期望构建脚本不要修改OUT_DIR之外的外部文件。提供的示例项目将生成的绑定输出到src/目录,用于文档目的。这种方法对于应用程序也是可行的。发布Crate应遵循Cargo Book中关于构建脚本的建议。

use wgsl_to_wgpu::{create_shader_module_embedded, WriteOptions};

// src/build.rs
fn main() {
    println!("cargo:rerun-if-changed=src/model.wgsl");

    // Generate the Rust bindings and write to a file.
    let text = create_shader_module_embedded(wgsl_source, WriteOptions::default()).unwrap();
    let out_dir = std::env::var("OUT_DIR").unwrap();
    std::fs::write(format!("{out_dir}/model.rs"), text.as_bytes()).unwrap();
}

生成的代码需要包含在其中一个正常源文件中。这包括根据需要添加任何嵌套模块。

// src/shader.rs
pub mod model {
    include!(concat!(env!("OUT_DIR"), "/model.rs"));
}

依赖关系

~6–15MB
~184K SLoC