我将尝试创建尽可能多的 MRE,但让 OpenGL 在 Rust 中工作相当复杂,所以我将跳过样板文件和一些不必要的细节。
我正在尝试创建一个计算着色器,该着色器执行一些光线行进(大量数据,我还不需要查看图像等,因此我将进行计算而不是简单的 vert->frag 着色器设置。 )
我经历了使用 glutin 和 glow 获取 gl 上下文的繁琐过程,然后我编译了我的着色器。着色器编译很好,在着色器编译和计算之间没有错误,无论是 gl 还是其他,但是当我查看计算的输出时,我目前只是简单地镜像了一些发送到着色器的数据,我得到的都是 0。
数据输出、统一输入和计算工作正常。我已经以各种方式验证了这一点。但是,大部分数据来自 SSB。输入数据和输出数据使用两个独立的 SSB。通过 SSB 输入似乎不起作用,我假设我在尝试发送到 GPU 时做错了什么。
Rust 代码的粗略模型:
// we have a &glow::Context
let ctx = // fun gl boilerplate stuff.
// create data struct
#[repr(C)]
struct Data {
// the layout of this struct might be the cause of the bug?
thing_1: [u32; 2],
thing_2: f32,
thing_3: [f32; 3],
}
// impl Default for Data
// setup buffer and data
let buf = ctx.create_buffer().unwrap();
ctx.bind_buffer(glow::SHADER_STORAGE_BUFFER, Some(buf));
// there's a reason this is a vec. it should be irrelevant.
let mut lots_of_data = vec![Data::default(); 1024];
// modify the first entry for testing later
lots_of_data[0].thing_1 = [u32::MAX, u32::MAX];
lots_of_data[0].thing_2 = -1234.0;
lots_of_data[0].thing_3 = [f32::NAN, f32::NAN, f32::NAN];
// easiest way to send data to a buffer is with a u8 slice.
// this might also be where the problem lies. this is a fairly
// dense area of opengl's standard, and not particularly
// well-documented - certainly not in the context of rust. so,
// I'm unsure if the problem lies in the way i'm passing data
// or somewhere else entirely, and i'm not really sure how to
// tell.
let data_u8 = std::slice::from_raw_parts(
lots_of_data.as_ptr().cast(),
lots_of_data.len() * std::mem::size_of::<Data>(),
);
ctx.buffer_data_u8_slice(
glow::SHADER_STORAGE_BUFFER,
data_u8,
glow::DYNAMIC_COPY
);
// repeat the above process for an output buffer
let program = ctx.create_program().unwrap();
// do shader boilerplate stuff
// use program, set uniforms, etc.
ctx.use_program(Some(program));
let location = ctx.get_uniform_location(program, "u_test");
ctx.uniform_1_f32(location.as_ref(), 42.0); // set uniform to something unique
ctx.bind_buffer_base(glow::SHADER_STORAGE_BUFFER, 0, Some(buf));
// we glossed over creating out_buf, but here we're binding it for output
ctx.bind_buffer_base(glow::SHADER_STORAGE_BUFFER, 1, Some(out_buf));
// run the shader
ctx.dispatch_compute(1000, 1, 1);
// for simplicity's sake, let's say my output data is a vec4
let mut out_raw = vec![0u8; 1024 * std::mem::size_of::<[f32; 4]>()];
// convert the u8s back into "vec4"s
ctx.get_buffer_sub_data(glow::SHADER_STORAGE_BUFFER, 0, &mut data_raw);
let out: &[[f32; 4]] = std::slice::from_raw_parts(
out_raw.as_ptr().cast(),
1024
);
// 1024 instances of [42.0, 0.0, 0.0, 0.0]. yes, even the 1st instance. no idea why.
dbg!(out);
着色器的粗略模型:
#version 440
layout(local_size_x = 8) in; // maybe this line? ngl, i copy+pasted
// same layout as Rust struct. maybe?
struct Data {
uvec2 thing1;
float thing2;
vec3 thing_3;
};
// create SSBs
layout(shared, binding = 0) readonly buffer InputData { Data input_data[]; };
layout(shared, binding = 1) writeonly buffer OutputData { vec4 output_data[]; };
// uniforms
uniform float u_test;
void main() {
// grab this thread's data
Data data = input_data[gl_GlobalInvocationID.x];
// create easily recognizable output data
vec4 out = vec4(
u_test,
float(data.thing1.x),
data.thing2,
data.thing3.x
);
// send output data
output_data[gl_GlobalInvocationID.x] = out;
}