phosphor-crt 0.1.0

A real-time plotter of waveforms, imitating oscillscope CRTs
Documentation
use crate::autointensity::AutoIntensityResources;
use crate::intermediate_texture::IntermediateTextureResources;
use crate::lut_texture::LutTextureResources;
use crate::uniform_buffer::UniformBufferResources;
use crate::waveform_resources::{WaveformMode, WaveformResources};
use wgpu::{Device, RenderPipeline, ShaderModule, TextureFormat};

#[derive(Debug)]
pub(crate) struct PipelineResources {
    render_pipeline: RenderPipeline,
    post_fx_pipeline: RenderPipeline,
    decay_pipeline: RenderPipeline,
    #[cfg(feature = "auto-intensity")]
    auto_intensity_pipeline: wgpu::ComputePipeline,
}

impl PipelineResources {
    #[allow(clippy::too_many_arguments)]
    pub(crate) fn new(
        render_shader: &ShaderModule,
        decay_shader: &ShaderModule,
        post_fx_shader: &ShaderModule,
        #[cfg(feature = "auto-intensity")] auto_intensity_shader: &ShaderModule,
        device: &Device,
        texture_format: &TextureFormat,
        intermediate_texture_data: &IntermediateTextureResources,
        lut_texture_data: &LutTextureResources,
        uniform_data: &UniformBufferResources,
        waveform_resources: &WaveformResources,
        auto_intensity_data: &AutoIntensityResources,
    ) -> PipelineResources {
        let render_pipeline =
            Self::create_render_pipeline(render_shader, device, uniform_data, waveform_resources);
        let post_fx_pipeline = Self::create_postfx_pipeline(
            post_fx_shader,
            device,
            texture_format,
            intermediate_texture_data,
            lut_texture_data,
            uniform_data,
            auto_intensity_data,
        );
        let decay_pipeline = Self::create_decay_pipeline(decay_shader, device, uniform_data);

        #[cfg(feature = "auto-intensity")]
        let auto_intensity_pipeline = Self::create_auto_intensity_pipeline(
            auto_intensity_shader,
            device,
            intermediate_texture_data,
            auto_intensity_data,
        );

        PipelineResources {
            render_pipeline,
            post_fx_pipeline,
            decay_pipeline,
            #[cfg(feature = "auto-intensity")]
            auto_intensity_pipeline,
        }
    }

    #[cfg(feature = "auto-intensity")]
    fn create_auto_intensity_pipeline(
        auto_intensity_shader: &ShaderModule,
        device: &Device,
        intermediate_texture_data: &IntermediateTextureResources,
        auto_intensity_data: &AutoIntensityResources,
    ) -> wgpu::ComputePipeline {
        device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
            label: Some("Auto Intensity Pipeline"),
            layout: Some(
                &device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
                    label: Some("Auto Intensity Compute Pipeline Layout"),
                    bind_group_layouts: &[
                        intermediate_texture_data.bind_group_layout(),
                        auto_intensity_data.bind_group_layout_compute(),
                    ],
                    push_constant_ranges: &[],
                }),
            ),
            module: auto_intensity_shader,
            entry_point: Some("main"),
            cache: None,
            compilation_options: Default::default(),
        })
    }

    fn create_postfx_pipeline(
        post_fx_shader: &ShaderModule,
        device: &Device,
        texture_format: &TextureFormat,
        intermediate_texture_data: &IntermediateTextureResources,
        lut_texture_data: &LutTextureResources,
        uniform_data: &UniformBufferResources,
        auto_intensity_data: &AutoIntensityResources,
    ) -> RenderPipeline {
        let bind_group_layouts = &[
            intermediate_texture_data.bind_group_layout(),
            lut_texture_data.bind_group_layout(),
            auto_intensity_data.bind_group_layout_fragment(),
            uniform_data.bind_group_layout(),
        ];

        let postfx_pipeline_layout =
            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
                label: Some("Post-processing Layout"),
                bind_group_layouts,
                push_constant_ranges: &[],
            });
        device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
            label: Some("Post-processing pipeline"),
            layout: Some(&postfx_pipeline_layout),
            vertex: wgpu::VertexState {
                module: post_fx_shader,
                entry_point: Some("postfx_vs_main"),
                buffers: &[],
                compilation_options: Default::default(),
            },
            fragment: Some(wgpu::FragmentState {
                module: post_fx_shader,
                entry_point: Some("postfx_fs_main"),
                targets: &[Some(wgpu::ColorTargetState {
                    format: *texture_format,
                    blend: Some(wgpu::BlendState {
                        alpha: wgpu::BlendComponent {
                            src_factor: wgpu::BlendFactor::One,
                            dst_factor: wgpu::BlendFactor::One,
                            operation: wgpu::BlendOperation::Add,
                        },
                        color: wgpu::BlendComponent {
                            src_factor: wgpu::BlendFactor::One,
                            dst_factor: wgpu::BlendFactor::One,
                            operation: wgpu::BlendOperation::Add,
                        },
                    }),
                    write_mask: wgpu::ColorWrites::ALL,
                })],
                compilation_options: Default::default(),
            }),
            primitive: wgpu::PrimitiveState {
                topology: wgpu::PrimitiveTopology::TriangleStrip,
                strip_index_format: None,
                front_face: wgpu::FrontFace::Ccw,
                cull_mode: None,
                // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE
                polygon_mode: wgpu::PolygonMode::Fill,
                // Requires Features::DEPTH_CLIP_CONTROL
                unclipped_depth: false,
                // Requires Features::CONSERVATIVE_RASTERIZATION
                conservative: false,
            },
            depth_stencil: None,
            multisample: wgpu::MultisampleState {
                count: 1,
                mask: !0,
                alpha_to_coverage_enabled: false,
            },
            multiview: None,
            cache: None,
        })
    }

    fn create_render_pipeline(
        render_shader: &ShaderModule,
        device: &Device,
        uniform_data: &UniformBufferResources,
        waveform_resources: &WaveformResources,
    ) -> RenderPipeline {
        let render_pipeline_layout =
            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
                label: Some("Pipeline Layout"),
                bind_group_layouts: &[uniform_data.bind_group_layout()],
                push_constant_ranges: &[],
            });
        device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
            label: Some("Render pipeline"),
            layout: Some(&render_pipeline_layout),
            vertex: wgpu::VertexState {
                module: render_shader,
                entry_point: Some(match waveform_resources.mode() {
                    WaveformMode::YT => "vs_yt_main",
                    WaveformMode::XY => "vs_xy_main",
                }),
                buffers: &[waveform_resources.buffer_layout()],
                compilation_options: Default::default(),
            },
            fragment: Some(wgpu::FragmentState {
                module: render_shader,
                entry_point: Some("fs_main"),
                targets: &[Some(wgpu::ColorTargetState {
                    format: TextureFormat::R16Float,
                    blend: Some(wgpu::BlendState {
                        alpha: wgpu::BlendComponent {
                            src_factor: wgpu::BlendFactor::One,
                            dst_factor: wgpu::BlendFactor::One,
                            operation: wgpu::BlendOperation::Add,
                        },
                        color: wgpu::BlendComponent {
                            src_factor: wgpu::BlendFactor::One,
                            dst_factor: wgpu::BlendFactor::One,
                            operation: wgpu::BlendOperation::Add,
                        },
                    }),
                    write_mask: wgpu::ColorWrites::ALL,
                })],
                compilation_options: Default::default(),
            }),
            primitive: wgpu::PrimitiveState {
                topology: wgpu::PrimitiveTopology::TriangleStrip,
                strip_index_format: None,
                front_face: wgpu::FrontFace::Ccw,
                cull_mode: None,
                // Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE
                polygon_mode: wgpu::PolygonMode::Fill,
                // Requires Features::DEPTH_CLIP_CONTROL
                unclipped_depth: false,
                // Requires Features::CONSERVATIVE_RASTERIZATION
                conservative: false,
            },
            depth_stencil: None,
            multisample: wgpu::MultisampleState {
                count: 1,
                mask: !0,
                alpha_to_coverage_enabled: false,
            },
            multiview: None,
            cache: None,
        })
    }

    pub(crate) fn render_pipeline(&self) -> &RenderPipeline {
        &self.render_pipeline
    }

    #[cfg(feature = "auto-intensity")]
    pub(crate) fn auto_intensity_pipeline(&self) -> &wgpu::ComputePipeline {
        &self.auto_intensity_pipeline
    }

    pub(crate) fn post_fx_pipeline(&self) -> &RenderPipeline {
        &self.post_fx_pipeline
    }

    pub(crate) fn decay_pipeline(&self) -> &RenderPipeline {
        &self.decay_pipeline
    }

    fn create_decay_pipeline(
        decay_shader: &ShaderModule,
        device: &Device,
        uniform_data: &UniformBufferResources,
    ) -> RenderPipeline {
        let decay_pipeline_layout =
            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
                label: Some("Decay Pipeline Layout"),
                bind_group_layouts: &[uniform_data.bind_group_layout()],
                push_constant_ranges: &[],
            });
        device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
            label: Some("Decay pipeline"),
            layout: Some(&decay_pipeline_layout),
            vertex: wgpu::VertexState {
                module: decay_shader,
                entry_point: Some("vs_main"),
                buffers: &[],
                compilation_options: Default::default(),
            },
            fragment: Some(wgpu::FragmentState {
                module: decay_shader,
                entry_point: Some("fs_main"),
                targets: &[Some(wgpu::ColorTargetState {
                    format: TextureFormat::R16Float,
                    blend: Some(wgpu::BlendState {
                        alpha: wgpu::BlendComponent {
                            src_factor: wgpu::BlendFactor::Zero,
                            dst_factor: wgpu::BlendFactor::SrcAlpha,
                            operation: wgpu::BlendOperation::Add,
                        },
                        color: wgpu::BlendComponent {
                            src_factor: wgpu::BlendFactor::Zero,
                            dst_factor: wgpu::BlendFactor::SrcAlpha,
                            operation: wgpu::BlendOperation::Add,
                        },
                    }),
                    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: None,
                polygon_mode: wgpu::PolygonMode::Fill,
                unclipped_depth: false,
                conservative: false,
            },
            depth_stencil: None,
            multisample: wgpu::MultisampleState {
                count: 1,
                mask: !0,
                alpha_to_coverage_enabled: false,
            },
            multiview: None,
            cache: None,
        })
    }
}