wgpu-noboiler 0.1.5

A simplified Api for wgpu and winit
Documentation
use std::borrow::Cow;
use std::fs;

use wgpu::{BindGroupLayout, BlendState, ColorTargetState, ColorWrites, DepthStencilState, Device, Face, FragmentState, FrontFace, MultisampleState, PipelineLayoutDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology, RenderPipeline, RenderPipelineDescriptor, ShaderModule, ShaderModuleDescriptor, ShaderSource, SurfaceConfiguration, TextureFormat, VertexBufferLayout, VertexState};

/// Builder Patter for wgpu [RenderPipeline]
pub struct RenderPipelineCreator<'a> {
    device: &'a Device,
    format: &'a TextureFormat,

    shader: ShaderModule,
    vertex_main: &'a str,
    fragment_main: &'a str,

    vertex_buffers: Vec<VertexBufferLayout<'a>>,
    bind_groups: Vec<&'a BindGroupLayout>,

    depth_stencil: Option<DepthStencilState>,

    label: &'a str,

    blend_state: BlendState,
}

impl<'a> RenderPipelineCreator<'a> {
    /// creates an [RenderPipelineCreator] where the shader is from the path
    pub fn from_shader_file(path: &'a str, device: &'a Device, config: &'a SurfaceConfiguration) -> RenderPipelineCreator<'a> {
        Self::from_shader_code(&fs::read_to_string(path).unwrap_or_else(|_| panic!("Could not find Shader-File at {}", path)), device, config)
    }

    /// creates an [RenderPipelineCreator] where the shader is based on the given code
    pub fn from_shader_code(shader_code: &str, device: &'a Device, config: &'a SurfaceConfiguration) -> RenderPipelineCreator<'a> {
        let shader = device.create_shader_module(ShaderModuleDescriptor {
            label: Some("Render Pipeline Shader"),
            source: ShaderSource::Wgsl(Cow::from(shader_code)),
        });

        RenderPipelineCreator {
            device,
            format: &config.format,
            shader,
            vertex_main: "vs_main",
            fragment_main: "fs_main",

            vertex_buffers: vec![],
            bind_groups: vec![],

            depth_stencil: None,

            label: "Render Pipeline",
            blend_state: BlendState::REPLACE,
        }
    }

    /// adds a [VertexBufferLayout] to the used list
    pub fn add_vertex_buffer(mut self, layout: VertexBufferLayout<'a>) -> Self {
        self.vertex_buffers.push(layout);

        self
    }

    /// adds a [BindGroupLayout] to the used list
    pub fn add_bind_group(mut self, layout: &'a BindGroupLayout) -> Self {
        self.bind_groups.push(layout);

        self
    }

    /// sets the name of the Fragment-Main
    pub fn fragment_main(mut self, fn_name: &'a str) -> Self {
        self.fragment_main = fn_name;
        self
    }

    /// sets the name of the Vertex-Main
    pub fn vertex_main(mut self, fn_name: &'a str) -> Self {
        self.vertex_main = fn_name;
        self
    }

    /// sets the used [DepthStencilState]
    pub fn depth_stencil(mut self, depth_stencil: DepthStencilState) -> Self {
        self.depth_stencil = Some(depth_stencil);
        self
    }

    /// sets the used [BlendState]
    pub fn blend_state(mut self, blend_state: BlendState) -> Self {
        self.blend_state = blend_state;
        self
    }

    /// creates a [RenderPipeline]
    pub fn build(&self) -> RenderPipeline {
        let render_pipeline_layout = self.device.create_pipeline_layout(&PipelineLayoutDescriptor {
            label: Some(&(self.label.to_owned() + " Layout")),
            bind_group_layouts: &self.bind_groups[..],
            push_constant_ranges: &[],
        });

        self.device.create_render_pipeline(&RenderPipelineDescriptor {
            label: Some(self.label),
            layout: Some(&render_pipeline_layout),
            vertex: VertexState {
                module: &self.shader,
                entry_point: self.vertex_main,
                buffers: &self.vertex_buffers[..],
            },
            fragment: Some(FragmentState {
                module: &self.shader,
                entry_point: self.fragment_main,
                targets: &[Some(ColorTargetState {
                    format: self.format.to_owned(),
                    blend: Some(self.blend_state),
                    write_mask: ColorWrites::ALL,
                })],
            }),
            primitive: PrimitiveState {
                topology: PrimitiveTopology::TriangleList,
                strip_index_format: None,
                front_face: FrontFace::Ccw,
                cull_mode: Some(Face::Back),
                polygon_mode: PolygonMode::Fill,
                unclipped_depth: false,
                conservative: false,
            },
            depth_stencil: self.depth_stencil.to_owned(),
            multisample: MultisampleState {
                count: 1,
                mask: !0,
                alpha_to_coverage_enabled: false,
            },
            multiview: None,
        })
    }
}