dunge 0.3.0-alpha

Simple and portable 3d render library
Documentation
use {
    crate::{
        bind::TypedGroup,
        sl::{InputInfo, IntoModule, Module, Stages},
        state::State,
        types::{MemberType, VectorType},
    },
    std::marker::PhantomData,
    wgpu::{PipelineLayout, ShaderModule, VertexAttribute, VertexBufferLayout},
};

pub struct Shader<V> {
    inner: Inner,
    vert: PhantomData<V>,
}

impl<V> Shader<V> {
    pub(crate) fn new<M, A>(state: &State, module: M) -> Self
    where
        M: IntoModule<A, Vertex = V>,
    {
        Self {
            inner: Inner::new(state, module.into_module()),
            vert: PhantomData,
        }
    }

    pub(crate) fn id(&self) -> usize {
        self.inner.id
    }

    pub(crate) fn module(&self) -> &ShaderModule {
        &self.inner.module
    }

    pub(crate) fn layout(&self) -> &PipelineLayout {
        &self.inner.layout
    }

    pub(crate) fn buffers(&self) -> Box<[VertexBufferLayout]> {
        use wgpu::*;

        fn layout(Vertex { size, attributes }: &Vertex) -> VertexBufferLayout {
            VertexBufferLayout {
                array_stride: *size as BufferAddress,
                step_mode: VertexStepMode::Vertex,
                attributes,
            }
        }

        self.inner.vertex.iter().map(layout).collect()
    }

    pub(crate) fn groups(&self) -> &[TypedGroup] {
        &self.inner.groups
    }
}

struct Vertex {
    size: usize,
    attributes: Box<[VertexAttribute]>,
}

struct Inner {
    id: usize,
    module: ShaderModule,
    layout: PipelineLayout,
    vertex: Box<[Vertex]>,
    groups: Box<[TypedGroup]>,
}

impl Inner {
    fn new(state: &State, Module { cx, nm }: Module) -> Self {
        use {
            std::{borrow::Cow, iter},
            wgpu::*,
        };

        let module = {
            let desc = ShaderModuleDescriptor {
                label: None,
                source: ShaderSource::Naga(Cow::Owned(nm)),
            };

            state.device().create_shader_module(desc)
        };

        let visibility = |stages: Stages| {
            let mut out = ShaderStages::empty();
            out.set(ShaderStages::VERTEX, stages.vs);
            out.set(ShaderStages::FRAGMENT, stages.fs);
            out
        };

        let mut entries = vec![];
        let mut groups = vec![];
        for info in cx.groups() {
            entries.clear();
            for (binding, member) in iter::zip(0.., info.decl) {
                let entry = match member {
                    MemberType::Scalar(_) | MemberType::Vector(_) | MemberType::Matrix(_) => {
                        BindGroupLayoutEntry {
                            binding,
                            visibility: visibility(info.stages),
                            ty: BindingType::Buffer {
                                ty: BufferBindingType::Uniform,
                                has_dynamic_offset: false,
                                min_binding_size: None,
                            },
                            count: None,
                        }
                    }
                    MemberType::Tx2df => BindGroupLayoutEntry {
                        binding,
                        visibility: visibility(info.stages),
                        ty: BindingType::Texture {
                            sample_type: TextureSampleType::Float { filterable: true },
                            view_dimension: TextureViewDimension::D2,
                            multisampled: false,
                        },
                        count: None,
                    },
                    MemberType::Sampl => BindGroupLayoutEntry {
                        binding,
                        visibility: visibility(info.stages),
                        ty: BindingType::Sampler(SamplerBindingType::Filtering),
                        count: None,
                    },
                };

                entries.push(entry);
            }

            let desc = BindGroupLayoutDescriptor {
                label: None,
                entries: &entries,
            };

            let bind = state.device().create_bind_group_layout(&desc);
            let layout = TypedGroup::new(info.tyid, bind);
            groups.push(layout);
        }

        let groups = groups.into_boxed_slice();
        let layout = {
            let groups: Vec<_> = groups.iter().map(TypedGroup::bind).collect();
            let desc = PipelineLayoutDescriptor {
                bind_group_layouts: &groups,
                ..Default::default()
            };

            state.device().create_pipeline_layout(&desc)
        };

        let vertex = {
            let vert = |info: InputInfo| {
                let mut offset = 0;
                let mut shader_location = 0;
                let attr = |vecty| {
                    let format = match vecty {
                        VectorType::Vec2f => VertexFormat::Float32x2,
                        VectorType::Vec3f => VertexFormat::Float32x3,
                        VectorType::Vec4f => VertexFormat::Float32x4,
                        VectorType::Vec2u => VertexFormat::Uint32x2,
                        VectorType::Vec3u => VertexFormat::Uint32x3,
                        VectorType::Vec4u => VertexFormat::Uint32x4,
                        VectorType::Vec2i => VertexFormat::Sint32x2,
                        VectorType::Vec3i => VertexFormat::Sint32x3,
                        VectorType::Vec4i => VertexFormat::Sint32x4,
                    };

                    let attr = VertexAttribute {
                        format,
                        offset,
                        shader_location,
                    };

                    offset += format.size();
                    shader_location += 1;
                    attr
                };

                Vertex {
                    size: info.size,
                    attributes: info.decl.into_iter().map(attr).collect(),
                }
            };

            cx.inputs().map(vert).collect()
        };

        Self {
            id: state.next_shader_id(),
            module,
            layout,
            vertex,
            groups,
        }
    }
}