gloss_renderer/forward_renderer/render_passes/
blit_pass.rs1use 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#[include_wgsl_oil::include_wgsl_oil("../../../shaders/blit.wgsl")]
12mod shader_code {}
13
14pub struct BlitPass {
17 render_pipeline: wgpu::RenderPipeline,
18 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 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) .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 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 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 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 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 .add_entry_tex(wgpu::ShaderStages::FRAGMENT, wgpu::TextureSampleType::Float { filterable: true })
105 .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().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}