veloren_voxygen/render/pipelines/
postprocess.rs

1use super::super::{Consts, ExperimentalShader, GlobalsLayouts, PipelineModes};
2use bytemuck::{Pod, Zeroable};
3use vek::*;
4
5#[repr(C)]
6#[derive(Copy, Clone, Debug, Zeroable, Pod)]
7pub struct Locals {
8    proj_mat_inv: [[f32; 4]; 4],
9    view_mat_inv: [[f32; 4]; 4],
10}
11
12impl Default for Locals {
13    fn default() -> Self { Self::new(Mat4::identity(), Mat4::identity()) }
14}
15
16impl Locals {
17    pub fn new(proj_mat_inv: Mat4<f32>, view_mat_inv: Mat4<f32>) -> Self {
18        Self {
19            proj_mat_inv: proj_mat_inv.into_col_arrays(),
20            view_mat_inv: view_mat_inv.into_col_arrays(),
21        }
22    }
23}
24
25pub struct BindGroup {
26    pub(in super::super) bind_group: wgpu::BindGroup,
27}
28
29pub struct PostProcessLayout {
30    pub layout: wgpu::BindGroupLayout,
31    mat_tex_present: bool,
32}
33
34impl PostProcessLayout {
35    pub fn new(device: &wgpu::Device, pipeline_modes: &PipelineModes) -> Self {
36        let mut bind_entries = vec![
37            // src color
38            wgpu::BindGroupLayoutEntry {
39                binding: 0,
40                visibility: wgpu::ShaderStages::FRAGMENT,
41                ty: wgpu::BindingType::Texture {
42                    sample_type: wgpu::TextureSampleType::Float { filterable: true },
43                    view_dimension: wgpu::TextureViewDimension::D2,
44                    multisampled: false,
45                },
46                count: None,
47            },
48            wgpu::BindGroupLayoutEntry {
49                binding: 1,
50                visibility: wgpu::ShaderStages::FRAGMENT,
51                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
52                count: None,
53            },
54            // Depth source
55            wgpu::BindGroupLayoutEntry {
56                binding: 2,
57                visibility: wgpu::ShaderStages::FRAGMENT,
58                ty: wgpu::BindingType::Texture {
59                    sample_type: wgpu::TextureSampleType::Float { filterable: false },
60                    view_dimension: wgpu::TextureViewDimension::D2,
61                    multisampled: false,
62                },
63                count: None,
64            },
65            wgpu::BindGroupLayoutEntry {
66                binding: 3,
67                visibility: wgpu::ShaderStages::FRAGMENT,
68                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
69                count: None,
70            },
71            // Locals
72            wgpu::BindGroupLayoutEntry {
73                binding: 4,
74                visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
75                ty: wgpu::BindingType::Buffer {
76                    ty: wgpu::BufferBindingType::Uniform,
77                    has_dynamic_offset: false,
78                    min_binding_size: None,
79                },
80                count: None,
81            },
82        ];
83
84        let mut binding = 5;
85        if pipeline_modes.bloom.is_on() {
86            bind_entries.push(
87                // src bloom
88                wgpu::BindGroupLayoutEntry {
89                    binding,
90                    visibility: wgpu::ShaderStages::FRAGMENT,
91                    ty: wgpu::BindingType::Texture {
92                        sample_type: wgpu::TextureSampleType::Float { filterable: true },
93                        view_dimension: wgpu::TextureViewDimension::D2,
94                        multisampled: false,
95                    },
96                    count: None,
97                },
98            );
99            binding += 1;
100        }
101        let mat_tex_present = pipeline_modes
102            .experimental_shaders
103            .contains(&ExperimentalShader::GradientSobel);
104        if mat_tex_present {
105            // Material source
106            bind_entries.push(wgpu::BindGroupLayoutEntry {
107                binding,
108                visibility: wgpu::ShaderStages::FRAGMENT,
109                ty: wgpu::BindingType::Texture {
110                    sample_type: wgpu::TextureSampleType::Uint,
111                    view_dimension: wgpu::TextureViewDimension::D2,
112                    multisampled: false,
113                },
114                count: None,
115            });
116        }
117
118        Self {
119            layout: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
120                label: None,
121                entries: &bind_entries,
122            }),
123            mat_tex_present,
124        }
125    }
126
127    pub fn bind(
128        &self,
129        device: &wgpu::Device,
130        src_color: &wgpu::TextureView,
131        src_depth: &wgpu::TextureView,
132        src_mat: &wgpu::TextureView,
133        src_bloom: Option<&wgpu::TextureView>,
134        sampler: &wgpu::Sampler,
135        depth_sampler: &wgpu::Sampler,
136        locals: &Consts<Locals>,
137    ) -> BindGroup {
138        let mut entries = vec![
139            wgpu::BindGroupEntry {
140                binding: 0,
141                resource: wgpu::BindingResource::TextureView(src_color),
142            },
143            wgpu::BindGroupEntry {
144                binding: 1,
145                resource: wgpu::BindingResource::Sampler(sampler),
146            },
147            wgpu::BindGroupEntry {
148                binding: 2,
149                resource: wgpu::BindingResource::TextureView(src_depth),
150            },
151            wgpu::BindGroupEntry {
152                binding: 3,
153                resource: wgpu::BindingResource::Sampler(depth_sampler),
154            },
155            wgpu::BindGroupEntry {
156                binding: 4,
157                resource: locals.buf().as_entire_binding(),
158            },
159        ];
160        let mut binding = 5;
161        // Optional bloom source
162        if let Some(src_bloom) = src_bloom {
163            entries.push(
164                // TODO: might be cheaper to premix bloom at lower resolution if we are doing
165                // extensive upscaling
166                // TODO: if there is no upscaling we can do the last bloom upsampling in post
167                // process to save a pass and the need for the final full size bloom render target
168                wgpu::BindGroupEntry {
169                    binding,
170                    resource: wgpu::BindingResource::TextureView(src_bloom),
171                },
172            );
173            binding += 1;
174        }
175        if self.mat_tex_present {
176            entries.push(wgpu::BindGroupEntry {
177                binding,
178                resource: wgpu::BindingResource::TextureView(src_mat),
179            });
180        }
181
182        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
183            label: None,
184            layout: &self.layout,
185            entries: &entries,
186        });
187
188        BindGroup { bind_group }
189    }
190}
191
192pub struct PostProcessPipeline {
193    pub pipeline: wgpu::RenderPipeline,
194}
195
196impl PostProcessPipeline {
197    pub fn new(
198        device: &wgpu::Device,
199        vs_module: &wgpu::ShaderModule,
200        fs_module: &wgpu::ShaderModule,
201        surface_config: &wgpu::SurfaceConfiguration,
202        global_layout: &GlobalsLayouts,
203        layout: &PostProcessLayout,
204    ) -> Self {
205        common_base::span!(_guard, "PostProcessPipeline::new");
206        let render_pipeline_layout =
207            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
208                label: Some("Post process pipeline layout"),
209                push_constant_ranges: &[],
210                bind_group_layouts: &[&global_layout.globals, &layout.layout],
211            });
212
213        let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
214            label: Some("Post process pipeline"),
215            layout: Some(&render_pipeline_layout),
216            vertex: wgpu::VertexState {
217                module: vs_module,
218                entry_point: "main",
219                buffers: &[],
220            },
221            primitive: wgpu::PrimitiveState {
222                topology: wgpu::PrimitiveTopology::TriangleList,
223                strip_index_format: None,
224                front_face: wgpu::FrontFace::Ccw,
225                cull_mode: None,
226                unclipped_depth: false,
227                polygon_mode: wgpu::PolygonMode::Fill,
228                conservative: false,
229            },
230            depth_stencil: None,
231            multisample: wgpu::MultisampleState {
232                count: 1,
233                mask: !0,
234                alpha_to_coverage_enabled: false,
235            },
236            fragment: Some(wgpu::FragmentState {
237                module: fs_module,
238                entry_point: "main",
239                targets: &[Some(wgpu::ColorTargetState {
240                    format: surface_config.format,
241                    blend: None,
242                    write_mask: wgpu::ColorWrites::ALL,
243                })],
244            }),
245            multiview: None,
246        });
247
248        Self {
249            pipeline: render_pipeline,
250        }
251    }
252}