noesis_wgpu 0.2.0

A wgpu render device for the Noesis GUI Native SDK: draws Noesis's batches on a wgpu device and queue you provide.
Documentation
//! Vertex buffer layouts for Noesis vertex formats, built from the SDK lookup
//! tables in `noesis_runtime::render_device::types`.
//!
//! Each Noesis vertex format is a bitmask of [`VertexAttr`] values. The
//! attributes are packed in `VertexAttr` order with no padding, and each
//! attribute's `shader_location` is its `VertexAttr` index, which is what
//! `noesis.wgsl`'s `VsIn` declares.
//!
//! [`VertexAttr`]: noesis_runtime::render_device::types::VertexAttr

use noesis_runtime::render_device::types::{
    ATTRIBUTES_FOR_FORMAT, SIZE_FOR_FORMAT, SIZE_FOR_TYPE, TYPE_FOR_ATTR, VERTEX_ATTR_COUNT,
};

/// Attribute list for the raw `VertexFormat::Enum` value `format_idx`, in
/// buffer order.
///
/// # Panics
///
/// Panics if `format_idx` is out of range for the SDK tables or a table names
/// an unknown attribute type.
#[must_use]
pub fn attributes_for_format(format_idx: u8) -> Vec<wgpu::VertexAttribute> {
    let mask = ATTRIBUTES_FOR_FORMAT[format_idx as usize];
    let mut attrs = Vec::new();
    let mut offset: u64 = 0;
    for attr_bit in 0..VERTEX_ATTR_COUNT as u8 {
        if (mask & (1 << attr_bit)) == 0 {
            continue;
        }
        let type_idx = TYPE_FOR_ATTR[attr_bit as usize];
        attrs.push(wgpu::VertexAttribute {
            offset,
            shader_location: u32::from(attr_bit),
            format: wgpu_format_for_type(type_idx),
        });
        offset += u64::from(SIZE_FOR_TYPE[type_idx as usize]);
    }
    debug_assert_eq!(
        offset,
        u64::from(SIZE_FOR_FORMAT[format_idx as usize]),
        "vertex layout stride mismatch for format {format_idx}"
    );
    attrs
}

/// Byte stride of one vertex in the `format_idx` format.
///
/// # Panics
///
/// Panics if `format_idx` is out of range for the SDK tables.
#[must_use]
pub fn stride_for_format(format_idx: u8) -> u64 {
    u64::from(SIZE_FOR_FORMAT[format_idx as usize])
}

fn wgpu_format_for_type(type_idx: u8) -> wgpu::VertexFormat {
    match type_idx {
        0 => wgpu::VertexFormat::Float32,   // VertexAttrType::Float
        1 => wgpu::VertexFormat::Float32x2, // VertexAttrType::Float2
        2 => wgpu::VertexFormat::Float32x4, // VertexAttrType::Float4
        3 => wgpu::VertexFormat::Unorm8x4,  // VertexAttrType::UByte4Norm
        4 => wgpu::VertexFormat::Unorm16x4, // VertexAttrType::UShort4Norm
        other => panic!("unknown VertexAttrType: {other}"),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn pos_color_layout() {
        // VertexFormat::PosColor (idx 1): Pos@0 (Float32x2, 8B) + Color@1 (Unorm8x4, 4B)
        let attrs = attributes_for_format(1);
        assert_eq!(attrs.len(), 2);
        assert_eq!(attrs[0].shader_location, 0);
        assert_eq!(attrs[0].offset, 0);
        assert_eq!(attrs[0].format, wgpu::VertexFormat::Float32x2);
        assert_eq!(attrs[1].shader_location, 1);
        assert_eq!(attrs[1].offset, 8);
        assert_eq!(attrs[1].format, wgpu::VertexFormat::Unorm8x4);
        assert_eq!(stride_for_format(1), 12);
    }

    #[test]
    fn pos_tex0_rect_tile_layout() {
        // VertexFormat::PosTex0RectTile (idx 4):
        //   Pos@0 (Float32x2, 8) + Tex0@2 (Float32x2, 8) +
        //   Rect@5 (Unorm16x4, 8) + Tile@6 (Float32x4, 16) = 40
        let attrs = attributes_for_format(4);
        assert_eq!(attrs.len(), 4);
        assert_eq!(attrs[0].shader_location, 0);
        assert_eq!(attrs[1].shader_location, 2);
        assert_eq!(attrs[2].shader_location, 5);
        assert_eq!(attrs[3].shader_location, 6);
        assert_eq!(stride_for_format(4), 40);
    }
}