写点什么

第一个 Shader 完成!

作者:Miracle
  • 2025-09-25
    四川
  • 本文字数:2051 字

    阅读完需:约 7 分钟

第一个 Shader 完成!

类型转换的基本原则之前已经说过了

浮点优先

符号优先

宽度优先

基本上就这三个原则,我们我们两个操作数 类型转换的逻辑如下


pub fn binary(&mut self, s1: &Symbol, s2: &Symbol)-> Result<(Type, u32, u32, u32)> {        let v1 = self.get(s1)?;        let s1 = self.get_var(v1);        let v2 = self.get(s2)?;        let s2 = self.get_var(v2);        if s1.1.is_f64() | s2.1.is_f64() {            let ty = Type::F64;            let ty_id = self.get_type(SpirvType::Var(ty.clone()));            if !s2.1.is_float() {                Ok((ty, ty_id, s1.0, self.builder.convert_u_to_f(ty_id, None, s2.0)?))            } else if !s1.1.is_float() {                Ok((ty, ty_id, self.builder.convert_u_to_f(ty_id, None, s1.0)?, s2.0))            } else {                Ok((ty, ty_id, s1.0, s2.0))            }        } else if s2.1.is_f32() {            let ty = Type::F32;            let ty_id = self.get_type(SpirvType::Var(ty.clone()));            if !s2.1.is_float() {                Ok((ty, ty_id, s1.0, self.builder.convert_u_to_f(ty_id, None, s2.0)?))            } else if !s1.1.is_float() {                Ok((ty, ty_id, self.builder.convert_u_to_f(ty_id, None, s1.0)?, s2.0))            } else {                Ok((ty, ty_id, s1.0, s2.0))            }        } else if s1.1.is_int() | s2.1.is_int() {            let width = s1.1.width().max(s2.1.width());            let ty = match width {                1=> Type::I8,                2=> Type::I16,                4=> Type::I32,                8=> Type::I64,                _ => panic!("Unsupported integer width {}", width),            };            let ty_id = self.get_type(SpirvType::Var(ty.clone()));            Ok((ty, ty_id, s1.0, s2.0))        } else if s1.1.is_uint() && s2.1.is_uint() {            let width = s1.1.width().max(s2.1.width());            let ty = match width {                1=> Type::U8,                2=> Type::U16,                4=> Type::U32,                8=> Type::U64,                _ => panic!("Unsupported integer width {}", width),            };            let ty_id = self.get_type(SpirvType::Var(ty.clone()));            Ok((ty, ty_id, s1.0, s2.0))        }        else {            panic!("{:?} {:?} {}", s1.1, s2.1, s2.0)        }    }
复制代码

前面几行是将符号转换为值,从 Builder 维护的 数组获取

后面的代码看似复杂,实际上就是简单的类型罗列,就不用细说了。

然后在实际做的时候,又碰到一个大坑 将 SSA 的数据存放到指针的时候,很有可能出现类型的细节不同,比如说我们代码里面 默认 取的 都是 i32,但是输入输出数组的时候,使用的是 u32

所以这里需要做一次强制转化,因为对于 Shader 编程 显然我们是知道自己在做什么的

                        let dest_ty = var.ty.get_type();                        if src.ty.get_type() != dest_ty {                   //如果类型不一样 强制转换                            let ty_id = self.get_type(super::SpirvType::Var(dest_ty));                            let id = self.builder.bitcast(ty_id, None, src.id)?;                            self.builder.store(var.id, id, None, None)?;                        } else {                            self.builder.store(var.id, src.id, None, None)?;
复制代码

这里 用 bitcast 直接转换


最后到了激动人心的运行时刻了

我们的 Zeta 代码是这样的

    pub struct Param {        id: [u32; 1024],    }
pub fn main(param: Param) { //let id = spirv_group(); let id = spirv_local(); let z = id[1] * 32 + id[0]; param.id[z] = z; }
复制代码

简单来说 就是 取出当前 的 x y 下标 然后 按照唯一顺序写入 缓冲区

        let mut b = spirv::SpirvBuilder::default();        println!("{:?}", b.import_module(Module::from(compiler), [32, 32, 1]));        //println!("{}", b.disassemble());        let spirv_code = b.assemble();        let mut rt = spirv::Runtime::default();        let buf = rt.run::<[u32; 1024], _>(&spirv_code, |buf| {} , [1, 1, 1])?;        println!("{:?}", buf.read().unwrap()[0]);
复制代码

我们的宿主代码是这样的,生成 Shader 的时候 指定 x y z 维度为 [32, 32, 1]

然后只创建一个 组 x,y,z 都是 1 这样正好 32 x 32 1024 个线程

执行结果是这样的


完美!!!!!里程碑时刻,第一个 Zeta 程序成功的在显卡上运行!!!!

用户头像

Miracle

关注

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

还未添加个人简介

评论

发布
暂无评论
第一个 Shader 完成!_Miracle_InfoQ写作社区