mepeyew 0.3.5

mepeyew - Small But Powerful Graphics Library
Documentation
use super::*;

impl WebGpuContext {
    pub fn new_program(
        &mut self,
        shaders: &ShaderSet,
        uniforms: &[ShaderUniform],
        ext: Option<NewProgramExt>,
    ) -> GResult<ProgramId> {
        let program = WebGpuProgram::new(self, shaders, uniforms, ext)?;
        self.programs.push(program);
        Ok(ProgramId::from_id(self.programs.len() - 1))
    }
}

pub struct WebGpuProgram {
    pub vertex_module: GpuShaderModule,
    pub fragment_module: Option<GpuShaderModule>,
    pub bind_groups: WebGpuBindGroups,
    pub vertex_buffer_layout: GpuVertexBufferLayout,
    pub ext: NewProgramExt,

    pub original_uniforms: Vec<ShaderUniform>,
}

impl WebGpuProgram {
    pub fn new(
        context: &WebGpuContext,
        shaders: &ShaderSet,
        uniforms: &[ShaderUniform],
        ext: Option<NewProgramExt>,
    ) -> GResult<Self> {
        let (ty, vertex) = take_single_shader(&context.device, shaders, |ty| {
            matches!(ty, ShaderType::Vertex(_))
        })?
        .ok_or(gpu_api_err!("webgpu did not get a vertex shader"))?;

        let fragment = take_single_shader(&context.device, shaders, |ty| {
            matches!(ty, ShaderType::Fragment)
        })?
        .map(|(_, shader)| shader);

        let vertex_buffer_layout_attributes = Array::new();

        let vertex_buffer_layout_array_stride = if let ShaderType::Vertex(vertex_data) = ty {
            let mut accum_stride = 0;
            let vertex_size = std::mem::size_of::<VertexBufferElement>();
            assert_eq!(vertex_size, std::mem::size_of::<f32>());

            for (location, arg) in vertex_data.args.iter().enumerate() {
                let format = match arg {
                    1 => GpuVertexFormat::Float32,
                    2 => GpuVertexFormat::Float32x2,
                    3 => GpuVertexFormat::Float32x3,
                    4 => GpuVertexFormat::Float32x4,
                    _ => Err(gpu_api_err!(
                        "webgpu an argument count of {} is invalid for vertex buffers",
                        arg
                    ))?,
                };
                let vertex_attr =
                    GpuVertexAttribute::new(format, accum_stride as f64, location as u32);
                accum_stride += arg * vertex_size;
                vertex_buffer_layout_attributes.push(&vertex_attr);
            }

            accum_stride as f64
        } else {
            unreachable!()
        };

        let vertex_buffer_layout = GpuVertexBufferLayout::new(
            vertex_buffer_layout_array_stride,
            &vertex_buffer_layout_attributes,
        );

        let bind_groups = WebGpuBindGroups::new(context, uniforms, false)?;

        Ok(WebGpuProgram {
            vertex_module: vertex,
            fragment_module: fragment,

            bind_groups,
            vertex_buffer_layout,
            ext: ext.unwrap_or_default(),

            original_uniforms: uniforms.to_vec(),
        })
    }

    pub fn recreate_bind_groups(&mut self, context: &WebGpuContext) -> GResult<()> {
        self.bind_groups = WebGpuBindGroups::new(context, &self.original_uniforms, false)?;
        Ok(())
    }
}

fn take_single_shader<F>(
    device: &GpuDevice,
    shaders: &ShaderSet,
    compare_shader_ty: F,
) -> GResult<Option<(ShaderType, GpuShaderModule)>>
where
    F: Fn(&ShaderType) -> bool,
{
    let mut ret_ty = None;
    let list = shaders
        .0
        .iter()
        .filter_map(|(ty, src)| {
            if compare_shader_ty(ty) {
                let shader_module_info =
                    GpuShaderModuleDescriptor::new(std::str::from_utf8(src).unwrap());
                let shader_module = device.create_shader_module(&shader_module_info);
                ret_ty = Some(ty.clone());
                Some(shader_module)
            } else {
                None
            }
        })
        .collect::<Vec<_>>();
    if list.len() > 1 {
        Err(gpu_api_err!("webgpu got multiple of the same shader type"))?;
    }
    Ok(list.get(0).cloned().map(|s| (ret_ty.unwrap(), s)))
}