roast2d_internal 0.4.0

Roast2D internal crate
Documentation
use std::borrow::Cow;

use crate::prelude::renderer_types::{TextureResource, Vertex};
use crate::prelude::wgpu::{self, Device, RenderPass};
use wgpu::{BindGroup, BindGroupLayout, RenderPipeline, ShaderModule, TextureFormat};

use crate::platform::types::CameraUniform;
use crate::render::BackendState;

use crate::renderer::traits::{Shader, SpritePipeline};

pub struct DefaultPipeline {
    pipeline: RenderPipeline,
    camera_bind_group: BindGroup,
    device: Device,
    texture_bind_group_layout: BindGroupLayout,
}

impl SpritePipeline for DefaultPipeline {
    fn render(
        &self,
        pass: &mut RenderPass,
        texture: Option<&TextureResource>,
        _shader_data: Option<&[u8]>,
    ) {
        pass.set_pipeline(&self.pipeline);
        pass.set_bind_group(0, &self.camera_bind_group, &[]);
        let texture = texture.expect("texture required for DefaultPipeline");
        let texture_bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
            layout: &self.texture_bind_group_layout,
            entries: &[
                wgpu::BindGroupEntry {
                    binding: 0,
                    resource: wgpu::BindingResource::TextureView(&texture.view),
                },
                wgpu::BindGroupEntry {
                    binding: 1,
                    resource: wgpu::BindingResource::Sampler(&texture.sampler),
                },
            ],
            label: Some("Texture Bind Group (default)"),
        });
        pass.set_bind_group(1, &texture_bind_group, &[]);
    }
}

pub struct DefaultShader {
    device: Device,
    shader: ShaderModule,
    camera_bind_group_layout: BindGroupLayout,
    texture_bind_group_layout: BindGroupLayout,
}

impl DefaultShader {
    pub const KEY: &'static str = "_roast2d_default";
    pub const SHADER: &'static str = include_str!("../../../assets/shaders/sprite.wgsl");

    pub fn new(state: &BackendState) -> Self {
        let device = state.device.clone();
        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
            label: Some(Self::KEY),
            source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(Self::SHADER)),
        });
        let camera_bind_group_layout =
            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
                entries: &[wgpu::BindGroupLayoutEntry {
                    binding: 0,
                    visibility: wgpu::ShaderStages::VERTEX,
                    ty: wgpu::BindingType::Buffer {
                        ty: wgpu::BufferBindingType::Uniform,
                        has_dynamic_offset: false,
                        min_binding_size: wgpu::BufferSize::new(
                            std::mem::size_of::<CameraUniform>() as _,
                        ),
                    },
                    count: None,
                }],
                label: Some("camera_bind_group_layout"),
            });

        let texture_bind_group_layout =
            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
                label: Some("Texture Bind Group Layout"),
                entries: &[
                    wgpu::BindGroupLayoutEntry {
                        binding: 0,
                        visibility: wgpu::ShaderStages::FRAGMENT,
                        ty: wgpu::BindingType::Texture {
                            sample_type: wgpu::TextureSampleType::Float { filterable: true },
                            view_dimension: wgpu::TextureViewDimension::D2,
                            multisampled: false,
                        },
                        count: None,
                    },
                    wgpu::BindGroupLayoutEntry {
                        binding: 1,
                        visibility: wgpu::ShaderStages::FRAGMENT,
                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
                        count: None,
                    },
                ],
            });

        Self {
            shader,
            camera_bind_group_layout,
            texture_bind_group_layout,
            device,
        }
    }
}

impl Shader for DefaultShader {
    fn key(&self) -> &'static str {
        Self::KEY
    }

    fn new_pipeline(
        &self,
        format: TextureFormat,
        depth_format: Option<TextureFormat>,
        camera_buffer: &wgpu::Buffer,
        transparent: bool,
    ) -> Box<dyn SpritePipeline> {
        let camera_bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
            layout: &self.camera_bind_group_layout,
            entries: &[wgpu::BindGroupEntry {
                binding: 0,
                resource: camera_buffer.as_entire_binding(),
            }],
            label: Some("camera_bind_group"),
        });
        let pipeline_layout = self
            .device
            .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
                label: Some("Render Pipeline Layout"),
                bind_group_layouts: &[
                    &self.camera_bind_group_layout,
                    &self.texture_bind_group_layout,
                ],
                push_constant_ranges: &[],
            });
        let pipeline = self
            .device
            .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
                cache: None,
                label: Some("Render Pipeline"),
                layout: Some(&pipeline_layout),
                vertex: wgpu::VertexState {
                    module: &self.shader,
                    entry_point: Some("vs_main"),
                    buffers: &[Vertex::desc()],
                    compilation_options: Default::default(),
                },
                fragment: Some(wgpu::FragmentState {
                    module: &self.shader,
                    entry_point: Some("fs_main"),
                    targets: &[Some(wgpu::ColorTargetState {
                        format,
                        blend: Some(wgpu::BlendState::ALPHA_BLENDING),
                        write_mask: wgpu::ColorWrites::ALL,
                    })],
                    compilation_options: Default::default(),
                }),
                primitive: wgpu::PrimitiveState {
                    topology: wgpu::PrimitiveTopology::TriangleList,
                    strip_index_format: None,
                    front_face: wgpu::FrontFace::Ccw,
                    cull_mode: Some(wgpu::Face::Back),
                    polygon_mode: wgpu::PolygonMode::Fill,
                    unclipped_depth: false,
                    conservative: false,
                },
                depth_stencil: depth_format.map(|format| wgpu::DepthStencilState {
                    format,
                    depth_write_enabled: !transparent,
                    depth_compare: wgpu::CompareFunction::Greater,
                    stencil: wgpu::StencilState::default(),
                    bias: wgpu::DepthBiasState::default(),
                }),
                multisample: wgpu::MultisampleState::default(),
                multiview: None,
            });
        let pipeline = DefaultPipeline {
            pipeline,
            camera_bind_group,
            device: self.device.clone(),
            texture_bind_group_layout: self.texture_bind_group_layout.clone(),
        };
        Box::new(pipeline)
    }
}