Skip to content

Procedural Macros to create VertexBufferLayout  #5621

Closed as not planned
Closed as not planned
@tgross35

Description

@tgross35

Is your feature request related to a problem? Please describe.

I think it is fairly common to have code like this:

#[repr(C)]
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
struct TessVertex {
    position: [f32; 2],
    normal: [f32; 2],
    prim_id: u32,
}

impl TessVertex {
    fn desc() -> wgpu::VertexBufferLayout<'static> {
        wgpu::VertexBufferLayout {
            array_stride: mem::size_of::<Self>() as wgpu::BufferAddress,
            step_mode: wgpu::VertexStepMode::Vertex,
            attributes: &[
                wgpu::VertexAttribute {
                    offset: 0,
                    shader_location: 0,
                    format: wgpu::VertexFormat::Float32x2,
                },
                wgpu::VertexAttribute {
                    offset: 8,
                    shader_location: 1,
                    format: wgpu::VertexFormat::Float32x2,
                },
                wgpu::VertexAttribute {
                    offset: 16,
                    shader_location: 2,
                    format: wgpu::VertexFormat::Uint32,
                },
            ],
        }
    }
}

This is a bit tedious to maintain but is fairly boilerplate, so a macro could help usability here.

Describe the solution you'd like

Probably extract the vertex format to a trait:

trait GpuFormat {
    const VERTEX_FORMAT: VertexFormat;
}

// implement this for relevant primitives
impl GpuFormat for f32 { const VERTEX_FORMAT: VertexFormat = VertexFormat::Float32; }
impl GpuFormat for [f32; 2] { const VERTEX_FORMAT: VertexFormat = VertexFormat::Float32x2; }
// ...

And then add a derive macro that leveragesGpuFormat and the recently-stabilized offset_of:

// trait to be implemented
trait BufferLayout {
    fn vertex_buffer_layout() -> VertexBufferLayout<'static>;
}

// usage

#[derive(BufferLayout)]
struct TessVertex {
    #[wgpu(shader_location = 0)]
    position: [f32; 2],
    #[wgpu(shader_location = 10)]
    normal: [f32; 2],
    #[wgpu(shader_location = 4)]
    prim_id: u32,
}

// code generated by the macro

impl BufferLayout for TessVertex {
    fn vertex_buffer_layout() -> VertexBufferLayout<'static> {
        VertexBufferLayout {
            array_stride: mem::size_of::<Self>() as wgpu::BufferAddress,
            step_mode: wgpu::VertexStepMode::Vertex,
            attributes: &[
                wgpu::VertexAttribute {
                    offset: std::mem::offset_of!(Self, position),
                    shader_location: 0,
                    format: [f32; 2]::VERTEX_FORMAT,
                },
                wgpu::VertexAttribute {
                    offset: std::mem::offset_of!(Self, normal),
                    shader_location: 10,
                    format: [f32; 2]::VERTEX_FORMAT,
                },
                wgpu::VertexAttribute {
                    offset: std::mem::offset_of!(Self, prim_id),
                    shader_location: 4,
                    format: u32::VERTEX_FORMAT,
                },
            ],
        }
    }
}

Note that this exact code above currently hits rust-lang/rust#124478, but that should be fixed soon in rust-lang/rust#124484.

Describe alternatives you've considered

It would be nice if more information could be reflected from the shader, rather than needing to keep the shader and the Rust code in sync (pretty big source of my errors when just getting started). Maybe an alternative to include_wgsl! could create a rust module with automatically created types for everything in WGSL shader?

I am sure this has been talked about somewhere :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions