第一个 Shader 完成!
作者:Miracle
- 2025-09-25 四川
本文字数:2051 字
阅读完需:约 7 分钟

类型转换的基本原则之前已经说过了
浮点优先
符号优先
宽度优先
基本上就这三个原则,我们我们两个操作数 类型转换的逻辑如下
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 程序成功的在显卡上运行!!!!
划线
评论
复制
发布于: 刚刚阅读数: 4
Miracle
关注
三十年资深码农 2019-10-25 加入
还未添加个人简介







评论