图形学编译器游戏开发【免费下载链接】rust-gpu Making Rust a first-class language and ecosystem for GPU shaders 项目地址https://gitcode.com/gh_mirrors/ru/rust-gpu点击查看免费下载本文面向希望借助 rust-gpu 项目快速上手 GPU 着色器开发的开发者系统梳理examples/目录下官方示例的完整构成着色器编写shaders、宿主端驱动运行runners与多模块构建multibuilder三大板块并深入剖析各示例在spirv-builder、spirv-std及 rustc 自定义代码生成器层面的实现细节帮助读者在最短时间内把一段 Rust 代码编译成 SPIR-V 并在 Vulkan/CPU 上运行起来。示例生态总览三驾马车各司其职examples/README.md将仓库内的所有示例划分为三个类别分别对应 Rust GPU 开发链路的三个关键环节目录角色定位核心关注点shaders/各种 rust-gpu 着色器源码展示如何用 Rust 编写 SPIR-V 着色器fragment/vertex/computerunners/构建并执行上述着色器的宿主程序展示如何把 rust-gpu 着色器集成进 Vulkanash/wgpu等构建体系并提供 CPU 侧无 GPU 运行参考multibuilder/spirv-buildermultimodule特性的极简示例展示单次构建产出多 SPIR-V 模块的用法值得注意runners下的程序并不完全是 rust-gpu 的使用示例——它们本质上是较为通用的 Vulkan 示例应用但包含了把 rust-gpu 着色器接入构建系统的基础设施代码同时作者在文档中坦诚这些示例并不是最干净的范例因为它们还顺带测试了一些较为绕弯的消费 rust-gpu 的方式。理解这一背景能帮你更准确地分辨哪些代码模式值得借鉴、哪些只是为了测试边界情况。shaders用 Rust 编写真实着色器shaders/目录是目前 rust-gpu 生态中最直接的着色器写法参考五个子 crate 覆盖了图形与计算两大赛道examples/shaders/ ├── simplest-shader/ # 最简片段/顶点着色器 ├── sky-shader/ # Preetham 大气散射天空着色器含 spec_constant 用法 ├── compute-shader/ # 计算着色器Collatz 猜想并行计算 ├── mouse-shader/ # 交互式 2D 矢量绘制SDF push constants ├── reduce/ # 子组归约subgroup reduce示例 └── shared/ # 共享工具库ShaderConstants 与数学辅助函数着色器 crate 的通用结构约定所有着色器 crate 都以近乎相同的头部开启这是值得在自建着色器 crate 中沿用的约定#![cfg_attr(target_arch spirv, no_std)] // HACK(eddyb) 否则无法从 spirv-builder 构建中看到警告。 #![deny(warnings)]以 simplest-shader/src/lib.rs 为例#![cfg_attr(target_arch spirv, no_std)]保证当代码以 SPIR-V 为目标架构编译时关闭标准库依赖#![deny(warnings)]则是为了在spirv-builder的构建输出里强制暴露所有警告源码注释解释了这一点。这是仓库源码可确认的通用模式sky-shader、compute-shader 等 crate 均采用同一写法。入口函数通过spirv_std::spirv宏声明着色器阶段与内置变量。最小化示例同时演示了顶点与片段两个阶段#[spirv(fragment)] pub fn main_fs(output: mut Vec4) { *output vec4(1.0, 0.0, 0.0, 1.0); // 输出纯红色 } #[spirv(vertex)] pub fn main_vs( #[spirv(vertex_index)] vert_id: i32, #[spirv(position, invariant)] out_pos: mut Vec4, ) { *out_pos vec4( (vert_id - 1) as f32, ((vert_id 1) * 2 - 1) as f32, 0.0, 1.0, ); }这里可以看到 rust-gpu 接口设计的核心思想把 SPIR-V 内置变量直接映射为 Rust 函数参数。#[spirv(vertex_index)]对应VertexIndex内置变量#[spirv(position)]对应Position输出内置变量可附加invariant等修饰。这正是 rust-gpu 与 GLSL 最大的差异点——接口不再是魔术全局变量而是类型安全、可由编译器检查的普通函数签名。compute-shader并行计算的正确打开方式compute-shader 改编自 wgpu 官方的 hello-compute 示例用一张storage_buffer完成输入输出#[spirv(compute(threads(64)))] pub fn main_cs( #[spirv(global_invocation_id)] id: UVec3, #[spirv(storage_buffer, descriptor_set 0, binding 0)] prime_indices: mut [u32], ) { let index id.x as usize; prime_indices[index] collatz(prime_indices[index]).unwrap_or(u32::MAX); }几个值得圈点的模式#[spirv(compute(threads(64)))]声明计算着色器threads(64)直接对应 HLSL 中的numthreads(64,1,1)或 GLSL 中的local_size_x 64通过#[spirv(storage_buffer, descriptor_set 0, binding 0)]声明存储缓冲描述符绑定——注意这里直接传递mut [u32]切片无需任何包装类型collatz函数用纯 Rust 实现了 Collatz 猜想迭代含3*n1的溢出防护并返回Optionu32失败时写入u32::MAX作为哨兵值。这段代码同时展示了在无std环境下 Rust 所有权与迭代逻辑如何被完整保留到 SPIR-V 中。值得一提该 crate 的 Cargo.toml 声明了crate-type [dylib, lib]并在cfg(not(target_arch spirv))条件下引入rayon——这意味着同一个 crate 既能编译成 SPIR-V 着色器又能在 CPU 侧作为普通 Rust 库被宿主程序直接调用是一份代码双端复用的关键设计。shared着色器与宿主共享的常量结构shaders/shared 是一个被其余着色器与 runners 共同依赖的小型工具库核心是ShaderConstants——通过#[repr(C)]bytemuck::Pod/Zeroable派生使其既能在 GPU 端作为 push constant 读取又能在 CPU 端零拷贝构造#[derive(Copy, Clone, Pod, Zeroable)] #[repr(C)] pub struct ShaderConstants { pub width: u32, pub height: u32, pub time: f32, pub cursor_x: f32, pub cursor_y: f32, // ... 拖拽起止点、鼠标按键位掩码、各按键最近按下时间 pub mouse_button_press_time: [f32; 3], }此外该库还提供了saturate、pow(Vec3, f32)、exp(Vec3)、smoothstep以及基于多项式近似的acos_approx等 GPU 友好型数学工具。acos_approx在源码注释中标注了依据 AMD GCN 架构的逆三角函数优化方案实现是显式性能换取近似精度的典型 GPU 编码手法。sky-shader大气散射 特化常量spec_constant集成测试sky-shader 移植自经典的 GLSL 天空着色器Tw1ddle/Sky-Shader实现了完整的 Preetham 大气散射模型Rayleigh 散射、Mie 散射、Henyey-Greenstein 相位函数、太阳圆盘合成与色调映射。它的更大价值在于——源码注释明确指出该文件充当特化常量的集成测试#[spirv(spec_constant(id 0x5007, default 100))] sun_intensity_extra_spec_const_factor: u32,spec_constant会生成 SPIR-VOpSpecConstant允许在运行时或通过构建期工具对常量做特化specialization以优化内核这里的正确结果仅在特化因子为1即乘以100/100的空操作时获得。入口函数main_fs还演示了如何组合frag_coord、push_constant与spec_constant三类输入#[spirv(fragment)] pub fn main_fs( #[spirv(frag_coord)] in_frag_coord: Vec4, #[spirv(push_constant)] constants: ShaderConstants, output: mut Vec4, #[spirv(spec_constant(id 0x5007, default 100))] sun_intensity_extra_spec_const_factor: u32, ) { ... }顶点着色器main_vs用vertex_index构造全屏三角形移植自 Sascha Willems 的无缓冲全屏四边形技巧#[spirv(position)]输出裁剪空间坐标。crate 底部还附有一个针对tonemap的单元测试可在 CPU 上验证数值正确性——再次印证同一份着色器代码可在宿主侧直接测试的设计哲学。mouse-shaderSDF 交互式绘图与高级类型抽象mouse-shader 是一个交互式 2D 矢量绘制示例用带符号距离函数SDF表达圆形、矩形、线段通过trait Shape抽象出union/intersect/stroke组合操作再叠加时间驱动的旋转场作为背景。它展示了 rust-gpu 的一个独特能力着色器中可以使用完整的 Rust 泛型与 trait 系统impl Shape这样的泛型参数在编译期被单态化这在 GLSL 中几乎不可想象。trait Shape: Copy { /// 负距离表示点在形状内部0 表示在边界上正距离表示在外部 fn distance(self, p: Vec2) - f32; fn unionS(self, other: S) - UnionSelf, S { ... } fn intersectS(self, other: S) - IntersectSelf, S { ... } fn stroke(self, thickness: f32) - StrokeSelf { ... } }其片段着色器从push_constantShaderConstants读取光标、拖拽与按键状态配合frag_coord完成逐像素求值最终通过 painter 模式混合输出 RGBA。reduce子组归约与内联汇编reduce 展示了更高阶的 GPU 能力子组subgroup级别的归约。它通过spirv_std::memory::Scope::Subgroup与内联汇编直接调用OpGroupNonUniformIAdd#[cfg(target_arch spirv)] pub unsafe fn subgroup_add(value: u32) - u32 { const EXECUTION: u32 Scope::Subgroup as _; let mut result 0; asm! { %u32 OpTypeInt 32 0, %execution OpConstant %u32 {execution}, %result OpGroupNonUniformIAdd _ %execution Reduce {value}, OpStore {result} %result, execution const EXECUTION, value in(reg) value, result in(reg) mut result, } result }非 SPIR-V 目标则提供panic!()的占位实现保证 crate 在宿主侧也能编译。着色器入口main同时使用了global_invocation_id、local_invocation_id、subgroup_local_invocation_id、workgroup_id、subgroup_id、num_subgroups六种内置变量以及#[spirv(workgroup)]声明的共享内存数组并在两阶段归约之间调用spirv_std::arch::workgroup_memory_barrier_with_group_sync()做组同步——这是理解 rust-gpu 底层内置变量与内联汇编写法的宝贵素材。runners三种方式把着色器跑起来runners/提供三个宿主端程序分别对应三种渲染/执行后端1. wgpu runner跨平台首选runners/wgpu 基于wgpufeatures [spirv]Cargo.toml支持在运行期通过 CLI 选择着色器--shader simplest|sky|compute|mouse # 默认 Sky --force-spirv-passthru # 强制直通 SPIR-V需 wgpu 支持构建环节在 wgpu/builder/src/main.rs 中完成——它自身是一个独立的build.rs-风格程序一次性用SpirvBuilder构建四个着色器 cratefn build_shader(path_to_crate: str, codegen_names: bool) - Result(), Boxdyn Error { let builder_dir Path::new(env!(CARGO_MANIFEST_DIR)); let path_to_crate builder_dir.join(path_to_crate); let result SpirvBuilder::new(path_to_crate, spirv-unknown-vulkan1.1).build()?; if codegen_names { let out_dir env::var_os(OUT_DIR).unwrap(); let dest_path Path::new(out_dir).join(entry_points.rs); fs::write(dest_path, result.codegen_entry_point_strings()).unwrap(); } Ok(()) }其中result.codegen_entry_point_strings()定义见 compile_result.rs把CompileResult::entry_points渲染为一棵可include!的路径树字符串再配合 lib.rs 中SpirvBuilder::watch(...)实现着色器源码变更后的热重载spirv-builder需开启watchfeature。CompiledShaderModules::spv_module_for_entry_point依据入口点名从构建结果中挑选对应 SPIR-V 模块并处理SingleModule与MultiModule两种返回形态ModuleResult枚举定义于 compile_result.rs。计算着色器路径见 compute.rs它会根据--force-spirv-passthru决定走device.create_shader_module_spirv直通还是 Naga 转译路径并请求wgpu::Features::TIMESTAMP_QUERY测量耗时。2. ash runner原生 Vulkan debugPrintfrunners/ash 是一个完整的手写ashVulkan渲染器包含实例/设备/交换链创建、验证层加载与全屏三角形渲染专门用于演示ShaderPanicStrategy::DebugPrintfThenExit调试管线。其compile_shaders函数展示了multimodule的真实使用场景源码注释明确标注这是debugPrintf插桩所必需参见 lib.rs 中对该已知限制的说明SpirvBuilder::new(examples/shaders/sky-shader, spirv-unknown-vulkan1.1) .print_metadata(MetadataPrintout::None) .shader_panic_strategy(spirv_builder::ShaderPanicStrategy::DebugPrintfThenExit { print_inputs: true, print_backtrace: true, }) .multimodule(true) .build() .unwrap() .module .unwrap_multi() .iter() .map(|(name, path)| SpvFile { name: format!(sky_shader::{name}), data: read_spv(...) }) .collect()随后以sky_shader::main_vs/sky_shader::main_fs这样的模块名::入口点形式把 SPIR-V 模块交给 Vulkan 管线对应 main.rs 中VertexShaderEntryPoint { module: sky_shader::main_vs.into(), entry_point: main_vs.into() }的结构。要真正看到 GPU 端debugPrintf输出需按 lib.rs 的说明启用 Vulkan Validation Layers 与VK_LAYER_ENABLESVK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT等环境变量。3. cpu runner无需 GPU 的纯 CPU 求值runners/cpu 是一个颇具启发性的降级运行器它不编译 SPIR-V而是直接以普通 Rust 库方式调用sky_shader::fs(...)利用前面提到的双端复用特性用rayon并行求值 1280×720 的每个像素再用minifb把结果画到窗口里let color shader_module::fs(push_constants, frag_coord, 1);为了让画面正确CPU 端还实现了线性到 sRGB 的 OETF 转换srgb_oetf。窗口标题为 Rust GPU - CPU shader evaluation。这种模式特别适合在没有 Vulkan 环境的 CI、服务器或教学场景里快速验证着色器算法。multibuilder多模块multimodule极简示例examples/multibuilder/是官方给出的spirv-builder的multimodule特性最小示例完整源码仅有十余行main.rsuse spirv_builder::{MetadataPrintout, SpirvBuilder}; fn main() { let result SpirvBuilder::new( concat!(env!(CARGO_MANIFEST_DIR), /../shaders/sky-shader), spirv-unknown-spv1.3, ) .print_metadata(MetadataPrintout::DependencyOnly) .multimodule(true) .build() .unwrap(); println!({result:#?}); }它演示了三个关键点路径拼接技巧concat!(env!(CARGO_MANIFEST_DIR), /../shaders/sky-shader)在编译期拼出着色器 crate 的绝对路径避免硬编码MetadataPrintout::DependencyOnly构建产物只保留依赖信息供多模块场景使用而不是全部 cargo 元数据.multimodule(true)把一个包含多个入口点sky-shader 同时有main_vs与main_fs的 crate 拆分输出为多个 SPIR-V 文件。multimodule 的底层行为从源码层面看multimodule选项在SpirvBuilder::new中默认为falselib.rs其文档说明将生成的 SPIR-V 文件按入口点拆分为多个模块。这在生态系统工具对单文件多入口点存在 bug 时非常有用——把全部入口点打包进单个文件是优先推荐的做法。拆分动作发生在链接阶段rustc_codegen_spirv的 link.rs 中链接器产生LinkResult::MultipleModules { ... }时CompileResult.module被置为ModuleResult::MultiModule(BTreeMapString, PathBuf)——一张入口点名 → 拆分后的 .spv 文件路径的映射表而默认的单文件路径则对应ModuleResult::SingleModule(PathBuf)。宿主程序据此可选择match compile_result.module { spirv_builder::ModuleResult::SingleModule(path) vec![(None, load_spv_module(path))], spirv_builder::ModuleResult::MultiModule(modules) modules .into_iter() .map(|(name, path)| (Some(name), load_spv_module(path))) .collect(), }这正是 wgpu runner 中handle_compile_result的实际处理逻辑lib.rs。什么时候必须开启 multimodule最典型的场景来自ShaderPanicStrategy::DebugPrintfThenExitdebugPrintf使用非语义扩展指令集SPV_KHR_non_semantic_infoSPIRV-Tools 的插桩工具对单文件多入口点存在已知限制KhronosGroup/SPIRV-Tools issue #4892因此 lib.rs 明确提示如果你有多个入口点可能需要开启multimodule。ash runner 与 wgpu runner 中的has_debug_printf路径正是这样做的。此外当第三方工具/驱动不能正确处理多入口点模块时拆分到单入口点文件往往是最稳妥的兼容手段——代价是产出多个.spv文件需要像spv_module_for_entry_point那样按入口点做分发。构建与运行最小闭环实操在阅读完上述源码后一个可落地的最小验证路径如下以本仓库为例# 1. 构建最简单的着色器并查看产物multibuilder 会打印 CompileResult 调试输出 cd examples/multibuilder cargo run # 2. 在 wgpu runner 中运行 sky-shader默认 cd examples/runners/wgpu cargo run -- --shader Sky # 3. 无 GPU 环境下用 CPU 求值验证 sky-shader 算法 cd examples/runners/cpu cargo run几点实用前提spirv-builder通过 multibuilder/Cargo.toml 中的 feature 控制工具链来源use-compiled-tools默认构建期编译 rustc_codegen_spirv 工具与use-installed-tools使用已安装的预编译工具适合省时场景spirv-builder构建时会写入cargo:rustc-env...等元数据print_metadata控制输出级别None/DependencyOnly/Full默认前文已结合多模块场景说明了各自用途wgpu runner 依赖 Vulkan SDK 或 MoltenVKmacOSwgpu需开启spirv特性见 Cargo.tomlspirv-builder的watch特性lib.rs 中build.rs风格 API支持在 wgpu runner 中实现着色器热更新修改 shaders 下源码后会自动重编译并重载。结语从示例到自有项目的最小迁移路径回看整个examples/目录一个清晰的 rust-gpu 落地路径浮出水面选一个与自己目标最接近的 shader 作为起点——做图形学演示从 simplest-shader 或 sky-shader 起步做通用计算从 compute-shader 起步需要 GPU 高级特性则参考 reduce复刻#![cfg_attr(target_arch spirv, no_std)]#![deny(warnings)]的 crate 骨架并把共享的 push-constant 结构体放进独立shared库参考 shared 的Pod/Zeroable#[repr(C)]模式用spirv-builder在build.rs或专用构建 crate 中驱动编译参考 wgpu/builder按需开启multimodule(true)、选择MetadataPrintout级别与ShaderPanicStrategy先上 CPU runner 做算法验证再接入 wgpu/ash 后端做真实 GPU 渲染避免在无 GPU 环境下阻塞开发。通过这一路径你可以把 rust-gpu 从读示例快速推进到写自己的着色器并复用仓库内经过验证的构建与运行基础设施。赞分享图形学编译器游戏开发【免费下载链接】rust-gpu Making Rust a first-class language and ecosystem for GPU shaders 项目地址https://gitcode.com/gh_mirrors/ru/rust-gpu点击查看免费下载相关推荐Rust-GPU 构建指南从源码编译 SPIR-V 着色器到运行示例Rust GPU 构建指南从源码编译 SPIR V 着色器到运行示例 本指南完整讲解如何在本地从源码构建 Rust GPU https://link.gitc图形学编译器游戏开发TypeGraphQL 官方示例仓库详解从运行方式到功能特性的实战全景TypeGraphQL 官方示例仓库详解从运行方式到功能特性的实战全景 本指南以 TypeGraphQL 仓库中的 examples/README.md ht后端GraphQLAPI设计rust-libp2p 官方示例实战指南从 Ping 到分布式 KV 存储的七个可运行案例rust libp2p 官方示例实战指南从 Ping 到分布式 KV 存储的七个可运行案例 examples/ 目录是 rust libp2p 仓库中所有官方网络通信后端上一篇从需求到上线基于ddddocr的验证码识别产品开发全流程下一篇Android-PickerView 自定义分割线样式从直线到圆形的创意设计创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考