写点什么

spirv 开端

作者:Miracle
  • 2025-09-24
    四川
  • 本文字数:1125 字

    阅读完需:约 4 分钟

spirv 开端

我们使用 rspirv 这个库生成 spirv 代码,使用 spirv 库 引入各种定义和基础函数


前面的章节中,我们已经描述了如何创建一个 type ID,然后用这个 type ID 创建一个变量

我们可以用下面的代码 创建一个函数 并给他一个名字


pub fn import_fn(&mut self, fn_def: FnDef)-> Result<()> { for arg in fn_def.args { self.add_arg(arg); } let ret_ty = self.get_type(Type::Void); let func_type_id = self.builder.type_function(ret_ty, Vec::new()); //main 函数是没有参数的 let func_id = self.builder.begin_function(ret_ty, None, spirv::FunctionControl::empty(), func_type_id)?; self.gen_block(&fn_def.code, None, None)?; self.builder.name(func_id, fn_def.name); self.builder.end_function()?; Ok(()) }
复制代码


这段代码 申明了函数类型,包括函数参数类型,返回值类型,以及有他们决定的正割函数的类型,然后使用这个函数类型 创建了一个 spirv 函数 ,给了一个名字

中间的 self.gen_block(&fn_def.code, None, None)?; 代码 生成函数体的代码,我们接下来详细描述

    pub fn assemble(self) -> Vec<u32> {        self.builder.module().assemble()    }}
复制代码

这段代码 将 builder 创建的 spirv 模块 汇编为 最终的 spirv 模块

注意这里面的参数 是 self 而不是我们通常用的 可写的 &mut self 以及只读的 &self 引用

这是因为 assemble 函数会消耗掉 builder 本身 也就是 builder 被转化为 module 了

这种用法 会避免数据的大量拷贝 只要 SpirvBuilder 以后不再用了

我们就尽可能使用 self


assemble 的结果是一个 Vec<u32> 的数组,这个 spirv 可以直接加载到显卡执行,当然我们现在完成的代码肯定一大堆错误,有可能把显卡烧坏哦


所以我们需要 先检查 生成的 spirv 代码

下面的代码使用我们 编译的 Zeta 中间代码,去掉符号信息之后生成 module 然后生成 spirv 二进制文件,存储到 "demo.spv" 之后反汇编


       let mut b = spirv::SpirvBuilder::default();        b.import_module(Module::from(compiler))?;        let spirv_code = b.assemble();        std::fs::write("demo.spv", bytemuck::cast_slice(&spirv_code))?;        let mut loader = rspirv::dr::Loader::new();        rspirv::binary::parse_words(&spirv_code, &mut loader).unwrap();        let parsed = loader.module();        println!("Disassembly:\n{}", parsed.disassemble());
复制代码


执行之后结果如下


完美,当然这个肯定有错,我们先用 Vulkan 的检查工具 spirv-val 检查一下,这玩意可以在 Vulkan 的官方 SDK 找到。


果然有错误

用户头像

Miracle

关注

三十年资深码农 2019-10-25 加入

还未添加个人简介

评论

发布
暂无评论
spirv 开端_Miracle_InfoQ写作社区