#Read Write Vec to Compute Shader

31 messages · Page 1 of 1 (latest)

tribal grail
#

Hi!
I am trying to create a boids compute shader and am getting stuck on how I should exactly go about passing a Vector of my Boids struct to the compute shader with read, write access. I store the vector of boid structs as a resource.

If I understood correctly, I am trying to create a BindGroupLayoutEntry with a binding type of buffer. Unfortunately when I try to prepare the bind group, I don't know how I should properly pass the resource.

I also don't really know how I can access the updated values of the buffer after it has ran the compute shader.

I've been struggling for multiple days so some help would really be appreciated. Some examples or documentation to read about this process would be great.

supple plover
tribal grail
# supple plover Did you see the game of life example? <https://github.com/bevyengine/bevy/blob/v...

Yes I've looked at the GOL example, but some parts I don't really know how to change for a vector of my given struct,

For the BindGroupLayoutEntry I figured it would be one of BindingType::Buffer:

BindGroupLayoutEntry {
    binding: 1,
    visibility: ShaderStages::COMPUTE,
    ty: BindingType::Buffer {
         ty: BufferBindingType::Storage {
               read_only: false,
         },
         has_dynamic_offset: false,
         min_binding_size: None,
    },
    count: None,
},

But I don't really know what I should add for it in the prepare_bind_group function, the texture gets added as an image asset to the images, which is then used to retrieve here. But I don't know how I would create a BindGroupEntry for my general struct Boid { pos, vel}.

Am I supposed to create a Buffer myself? I'm sure im missing something but I am not sure what and am struggling to find it.

I am also not entirely sure what to derives to use for my struct, the PostProcessing example uses a component but I think that is for uniforms and wouldn't let me write to it.

supple plover
#

Yes I think you'll need to create the wgpu buffer yourself. Not sure where it should be stored but I assume you can store it in your equivalent to "GameOfLifePipeline" see below, probably as its own resource or in the same resource as the bind group makes the most sense.
I assume you'll want to first populate it with the starting data, which you can do using bevy_render::render_device::create_buffer_with_data

Then you should be able to create a bind group and add the buffer you created as a bind group entry in it

#

Keep in mind I'm not super familiar with the bevy_render flavor of wgpu so take what I say with a grain of salt :x

supple plover
tribal grail
#

Alright, thank you for the help! I'll give it a shot when I have time later this evening hopefully.

supple plover
#

Np, let me know how it goes!

tribal grail
supple plover
#

I think you'd want to create a struct that represents the whole bindgroup (something like struct Boids { #[storage(0, buffer)] buffer: Buffer })... Might be overkill after all, you can always make the bind group manually

tribal grail
#

ooh okay

tribal grail
#

Im struggling to implement it properly, I tried to create a buffer from the vector in the prepare_bind_group function, but I dont know how I can get the content to be bytes &[u8],

I have made my Boid struct POD and repr(c)

#[derive(ShaderType, Pod, Zeroable, Clone, Copy)]
#[repr(C)]
struct Boid {
    pos: Vec3,
    vel: Vec3,
}

but still am not sure how to use it as contents:

let mut boids_vec: Vec<Boid> = Vec::with_capacity(BOIDS as usize);
for _ in 0..(BOIDS as usize) {
    boids_vec.push(Boid {
        pos: Vec3::new(0., 0., 0.),
        vel: Vec3::new(0., 0., 0.),
    });
}

let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
    label: Some("boids_buffer"),
    contents: boids_vec.into(), //Don't know how I should do this properly.
    usage: BufferUsages::COPY_DST | BufferUsages::COPY_SRC | BufferUsages::STORAGE,
});

I am a bit of a rust noob so forgive me if it's a dumb question.

I also saw that there is a StorageBuffer Type in bevy::render::render_resource, I would create the buffer using StorageBuffer::from, but then buffer.binding() would panic if I unwrapped.
I figured I probably needed to use write_buffer, but I don't know what Renderqueue I would give.

Thank you for the help already :)

supple plover
tribal grail
#

I've been able to get the buffer into my compute shader for now. Thank you so much for the help again. Will look into reading the buffer at a later time, but I believe I can just access the buffer through the resource I stored it in right?

supple plover
#

Ah that's good to hear 😄 No problem
Yes, you need a bit of weird async ceremony to read back from it, but you can check wgpu's hello-compute example to see how to do it (there might be a more bevy-specific way to do it, not sure)

tribal grail
#

Its been a while but I finally got some time to look at this again. I am struggling again however :(.

I've created a staging buffer like in the example in prepare_bind_group and have created a resource with it.

let staging_buffer = render_device.create_buffer(&BufferDescriptor {
   label: Some("boids_buffer"),
   size: buffer.size(),
   usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,
   mapped_at_creation: true
});

But when I try to get this resource in a function it is somehow never available.

fn read_buffer(boids_buffer: Option<Res<BoidsStagingBuffer>>) {

    match boids_buffer {
        Some(_) => {
            println!("Buffer is ready");
        }
        None => {
            println!("Not ready yet."); //Always prints this
        }
    }
}

Maybe its easier to do the same thing like the wgpu example in a function besides bevy at this point, but I wanna ensure it runs every frame

supple plover
#

is your read_buffer system in the render world?

#

*meant the RenderApp

tribal grail
#

I add the read_buffer to Update

#

Should I add it to RenderApp?

supple plover
#

Resources added inside the RenderApp are only accessible within it I believe

tribal grail
#

ooh okay

#

it indeed had to be added to the render pipeline, I'm only able to read 0's tho, do I still need to copy over the valuest o the staging buffer?

supple plover
tribal grail
#

How can I deal with the async functions in bevy

supple plover
tribal grail
#

Thank you for the help so far. I found a crate for bevy which allows you to setup a compute shader and add staging buffers more easily. Your help has helped me understand it better. When modifying this example in https://github.com/Kjolnyr/bevy_app_compute/blob/main/examples/boids.rs. I noticed that it works when the vectors in the Boid struct are either vec2 or vec4. But vec3 gives me an error when copying back to a Vec<Boids>. I thought repr(C) would help prevent this, but it doesn't seem to be the case. Is there any way I can make it work with Vec3?

supple plover
#

Ooh nice find, that crate looks nice 😮
Not sure why it wouldnt work with a vec3, I thought the crate would handle alignment using encase. As a workaround you could add a dummy f32 field after the vec3 though

crude grail