gloss_renderer/forward_renderer/render_passes/
blit_pass.rs

1use easy_wgpu::{
2    bind_group::{BindGroupBuilder, BindGroupDesc, BindGroupWrapper},
3    bind_group_layout::{BindGroupLayoutBuilder, BindGroupLayoutDesc},
4    gpu::Gpu,
5    pipeline::RenderPipelineDescBuilder,
6    texture::Texture,
7};
8use log::debug;
9
10//shaders
11#[include_wgsl_oil::include_wgsl_oil("../../../shaders/blit.wgsl")]
12mod shader_code {}
13
14/// Rendering pass which copies from a texture towards another one, resizing if
15/// necessary. Useful for copying the final rendered texture towards the screen.
16pub struct BlitPass {
17    render_pipeline: wgpu::RenderPipeline,
18    //per_pass things
19    sampler: wgpu::Sampler,
20    input_layout: wgpu::BindGroupLayout,
21    input_bind_group: Option<BindGroupWrapper>,
22}
23
24impl BlitPass {
25    pub fn new(gpu: &Gpu, surface_format: &wgpu::TextureFormat) -> Self {
26        let input_layout_desc = Self::input_layout_desc();
27        let input_layout = input_layout_desc.clone().into_bind_group_layout(gpu.device());
28
29        //render pipeline
30        let render_pipeline = RenderPipelineDescBuilder::new()
31            .label("blit pipeline")
32            .shader_code(shader_code::SOURCE)
33            .shader_label("blit_shader")
34            .add_bind_group_layout_desc(input_layout_desc) // for the texture we are sampling
35            .add_render_target(wgpu::ColorTargetState {
36                format: *surface_format,
37                blend: Some(wgpu::BlendState::REPLACE),
38                write_mask: wgpu::ColorWrites::ALL,
39            })
40            .depth_state(None)
41            .multisample(wgpu::MultisampleState::default())
42            .build_pipeline(gpu.device());
43
44        //sampler
45        let sampler = gpu.device().create_sampler(&wgpu::SamplerDescriptor {
46            mag_filter: wgpu::FilterMode::Linear,
47            min_filter: wgpu::FilterMode::Linear,
48            mipmap_filter: wgpu::FilterMode::Linear,
49            ..Default::default()
50        });
51
52        Self {
53            render_pipeline,
54            sampler,
55            input_layout,
56            input_bind_group: None,
57        }
58    }
59
60    /// # Panics
61    /// Will panic if the `input_bind_group` is not created. It should be
62    /// created automatically when doing ``run()`` by the `update_bind_group()`
63    pub fn run(&mut self, gpu: &Gpu, src_texture: &Texture, out_view: &wgpu::TextureView) {
64        let mut encoder = gpu
65            .device()
66            .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Blit Encoder") });
67
68        {
69            //update the bind group in case the input_texture changed
70            self.update_input_bind_group(gpu, src_texture);
71
72            {
73                let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
74                    label: Some("Blit Pass"),
75                    color_attachments: &[Some(wgpu::RenderPassColorAttachment {
76                        view: out_view,
77                        resolve_target: None,
78                        ops: wgpu::Operations {
79                            load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
80                            store: wgpu::StoreOp::Store,
81                        },
82                    })],
83                    depth_stencil_attachment: None,
84                    timestamp_writes: None,
85                    occlusion_query_set: None,
86                });
87
88                render_pass.set_pipeline(&self.render_pipeline);
89
90                render_pass.set_bind_group(0, self.input_bind_group.as_ref().unwrap().bg(), &[]);
91
92                //draw a quad
93                render_pass.draw(0..4, 0..1);
94            }
95        }
96
97        gpu.queue().submit(Some(encoder.finish()));
98    }
99
100    fn input_layout_desc() -> BindGroupLayoutDesc {
101        BindGroupLayoutBuilder::new()
102            .label("blit_texture_layout")
103            //input texture
104            .add_entry_tex(wgpu::ShaderStages::FRAGMENT, wgpu::TextureSampleType::Float { filterable: true })
105            //sampler
106            .add_entry_sampler(wgpu::ShaderStages::FRAGMENT, wgpu::SamplerBindingType::Filtering)
107            .build()
108    }
109
110    fn update_input_bind_group(&mut self, gpu: &Gpu, src_texture: &Texture) {
111        let entries = BindGroupBuilder::new()
112            .add_entry_tex(src_texture)
113            .add_entry_sampler(&self.sampler)
114            .build_entries();
115        // let stale = self.input_bind_group.as_ref().map_or(true, |b| b.is_stale(&entries)); //returns true if the bg has not been created or if stale
116        let stale = self.input_bind_group.as_ref().is_none_or(|b| b.is_stale(&entries));
117        if stale {
118            debug!("blit bind group is stale, recreating");
119            self.input_bind_group = Some(BindGroupDesc::new("blit_bg", entries).into_bind_group_wrapper(gpu.device(), &self.input_layout));
120        }
121    }
122}