teoxoy / encase

Provides a mechanism to lay out data into GPU buffers according to WGSL's memory layout rules
https://crates.io/crates/encase
MIT No Attribution
155 stars 27 forks source link

Vertex buffers #1

Open daxpedda opened 2 years ago

daxpedda commented 2 years ago

I am currently trying to use encase with wgpu, but got a bit confused when I tried to use it for vertex buffers.

As far as I could tell, the only difference between StorageBuffer and UniformBuffer is that UniformBuffer calls assert_uniform_compat.

So if I need to make a vertex buffer, am I supposed to just use StorageBuffer? (worked fine so far by the way)

teoxoy commented 2 years ago

Vertex buffers are a bit tricky to fit into the current design since they have a couple more limitations but also offer more flexibility at the same time. Only scalars and vectors should be allowed and there are also 8 bit and 16 bit variants that get mapped automatically to 32 bit ones in the shader.

I experimented with adding support for them a while ago but don't have anything concrete yet.

Do note that using the existing uniform/storage buffer warpers for this might not always work since vertex buffer items are tightly packed.

teoxoy commented 2 years ago

I'd personally use bytemuck (for vertex buffers) for now since there is no extra padding needed for them.

daxpedda commented 2 years ago

Thanks for the explanation, will do.

kaphula commented 1 year ago

I'd personally use bytemuck (for vertex buffers) for now since there is no extra padding needed for them.

Sorry for necrobump, but is there currently a solution for index buffers? As far as I know, if you have index buffer of u16 values for example, you need to pad them correctly. Casting arbitrarily sized index array through bytemuck can fail unlike vertex buffer.

Edit: Well I scrapped something for this case and it seems to work:

pub fn pad_index_buffer(buffer: &[u16]) -> Vec<u16> {
    let s = buffer.len() % 4;
    let mut gg = buffer.to_vec();
    if s > 0 {
        for _ in 0..s {
            gg.push(0);
        }
    }
    gg
}
teoxoy commented 1 year ago

@kaphula &[u16] to &[u8] via bytemuck should work, the reverse might not (if the size is odd).

simonask commented 2 weeks ago

I think a significant use case (at least one that I have) is generating/modifying vertex data in a compute shader, in which case the layout must be compatible with both structs in storage buffers and the vertex attribute layout. It would be useful to support the subset of vertex attribute formats that are representable in ShaderType, which mostly comes down to providing a way to query the offset of fields for wgpu::VertexAttribute.

In my own use case, I just have my own derive macro that can generate wgpu::VertexBufferLayout requiring bytemuck::Pod and using std::mem::offset_of!(), but I would like to write vertex data using ShaderType (because the same data is accessed as storage buffers in compute shaders), and there isn't really any way to determine that the layouts are compatible.

I would use the new is_pod flag, but that isn't set for user-defined types (and I'm not sure how that would be possible in #[derive(ShaderType)]).

Would it be feasible/useful to go the same route as uniform/storage compatibility checks, i.e. something like assert_vertex_compat() (panics on rts arrays) and assert_storage_compat() (panics if vertex data contains u16 etc)?