第一个 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 加入
还未添加个人简介
评论