1use crate::kvasir::node::{ExecutionContext, KvasirNode};
7use crate::kvasir::nodes::PassId;
8use crate::kvasir::resource::ResourceId;
9use crate::renderer::GpuRenderer;
10
11pub struct BackdropRegionNode {
14 pub inputs: Vec<ResourceId>,
15 pub outputs: Vec<ResourceId>,
16 pub region: cvkg_core::Rect,
18 pub output_id: ResourceId,
20}
21
22impl BackdropRegionNode {
23 pub fn new(region: cvkg_core::Rect, output_id: ResourceId) -> Self {
24 Self {
25 inputs: vec![crate::kvasir::nodes::RES_SCENE],
26 outputs: vec![output_id],
27 region,
28 output_id,
29 }
30 }
31}
32
33impl KvasirNode for BackdropRegionNode {
34 fn label(&self) -> &'static str {
35 "Backdrop Region"
36 }
37 fn inputs(&self) -> &[ResourceId] {
38 &self.inputs
39 }
40 fn outputs(&self) -> &[ResourceId] {
41 &self.outputs
42 }
43 fn pass_id(&self) -> PassId {
44 PassId::BackdropRegion
45 }
46 fn execute(&self, ctx: &mut ExecutionContext) {
47 let scene_tex = match ctx.registry.get_texture(crate::kvasir::nodes::RES_SCENE) {
48 Some(v) => v,
49 None => {
50 log::error!("[BackdropRegion] Missing scene texture");
51 return;
52 }
53 };
54 let blur_tex = match ctx.registry.get_texture(self.output_id) {
55 Some(v) => v,
56 None => {
57 log::error!("[BackdropRegion] Missing blur target texture");
58 return;
59 }
60 };
61
62 let scale = ctx.scale_factor;
63 let rx = (self.region.x * scale) as u32;
64 let ry = (self.region.y * scale) as u32;
65 let rw = (self.region.width * scale) as u32;
66 let rh = (self.region.height * scale) as u32;
67
68 let _src_extent = wgpu::Extent3d {
71 width: scene_tex.width(),
72 height: scene_tex.height(),
73 depth_or_array_layers: 1,
74 };
75 let dst_extent = wgpu::Extent3d {
76 width: rw,
77 height: rh,
78 depth_or_array_layers: 1,
79 };
80 ctx.encoder.copy_texture_to_texture(
81 wgpu::TexelCopyTextureInfo {
82 texture: &scene_tex,
83 mip_level: 0,
84 origin: wgpu::Origin3d { x: rx, y: ry, z: 0 },
85 aspect: wgpu::TextureAspect::All,
86 },
87 wgpu::TexelCopyTextureInfo {
88 texture: &blur_tex,
89 mip_level: 0,
90 origin: wgpu::Origin3d::ZERO,
91 aspect: wgpu::TextureAspect::All,
92 },
93 dst_extent,
94 );
95
96 let mip_count = blur_tex.mip_level_count().min(4);
100 if mip_count >= 2 {
101 let kawase_uniform = &ctx.renderer.kawase_uniform;
103
104 for mip in 1..mip_count {
105 let src_view = {
106 let mut cache =
107 GpuRenderer::lock_or_clear_cache(&ctx.renderer.texture_view_cache);
108 cache
109 .entry((ctx.renderer.current_window, self.output_id, (mip - 1)))
110 .or_insert_with(|| {
111 blur_tex.create_view(&wgpu::TextureViewDescriptor {
112 label: Some(&format!("blur_region_src_mip_{}", mip - 1)),
113 base_mip_level: mip - 1,
114 mip_level_count: Some(1),
115 ..Default::default()
116 })
117 })
118 .clone()
119 };
120 let dst_view = {
121 let mut cache =
122 GpuRenderer::lock_or_clear_cache(&ctx.renderer.texture_view_cache);
123 cache
124 .entry((ctx.renderer.current_window, self.output_id, mip))
125 .or_insert_with(|| {
126 blur_tex.create_view(&wgpu::TextureViewDescriptor {
127 label: Some(&format!("blur_region_dst_mip_{}", mip)),
128 base_mip_level: mip,
129 mip_level_count: Some(1),
130 ..Default::default()
131 })
132 })
133 .clone()
134 };
135
136 let w = (rw >> mip).max(1);
137 let h = (rh >> mip).max(1);
138 let kernel = mip as f32;
139
140 let uniform_data: [f32; 8] = [
141 w as f32,
142 h as f32,
143 (mip - 1) as f32,
144 kernel,
145 0.0,
146 0.0,
147 0.0,
148 0.0,
149 ];
150 ctx.queue
151 .write_buffer(kawase_uniform, 0, bytemuck::cast_slice(&uniform_data));
152
153 let src_bg = ctx.get_or_create_bind_group(
154 (self.output_id, mip, false),
155 &ctx.renderer.kawase_bind_group_layout,
156 &[
157 wgpu::BindGroupEntry {
158 binding: 0,
159 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
160 buffer: kawase_uniform,
161 offset: 0,
162 size: wgpu::BufferSize::new(32),
163 }),
164 },
165 wgpu::BindGroupEntry {
166 binding: 1,
167 resource: wgpu::BindingResource::TextureView(&src_view),
168 },
169 wgpu::BindGroupEntry {
170 binding: 2,
171 resource: wgpu::BindingResource::Sampler(&ctx.renderer.sampler),
172 },
173 ],
174 Some(&format!("blur_region_kawase_bg_{}", mip)),
175 );
176
177 let mut pass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
178 label: Some(&format!("Backdrop Region Blur {}", mip)),
179 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
180 view: &dst_view,
181 resolve_target: None,
182 ops: wgpu::Operations {
183 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
184 store: wgpu::StoreOp::Store,
185 },
186 depth_slice: None,
187 })],
188 ..Default::default()
189 });
190 pass.set_viewport(0.0, 0.0, w as f32, h as f32, 0.0, 1.0);
191 pass.set_pipeline(&ctx.renderer.kawase_down_pipeline);
192 pass.set_bind_group(0, &src_bg, &[]);
193 pass.draw(0..3, 0..1);
194 }
195
196 for mip in (1..mip_count).rev() {
198 let src_view = {
199 let mut cache =
200 GpuRenderer::lock_or_clear_cache(&ctx.renderer.texture_view_cache);
201 cache
202 .entry((ctx.renderer.current_window, self.output_id, mip))
203 .or_insert_with(|| {
204 blur_tex.create_view(&wgpu::TextureViewDescriptor {
205 label: Some(&format!("blur_region_src_mip_{}", mip)),
206 base_mip_level: mip,
207 mip_level_count: Some(1),
208 ..Default::default()
209 })
210 })
211 .clone()
212 };
213 let dst_view = {
214 let mut cache =
215 GpuRenderer::lock_or_clear_cache(&ctx.renderer.texture_view_cache);
216 cache
217 .entry((ctx.renderer.current_window, self.output_id, (mip - 1)))
218 .or_insert_with(|| {
219 blur_tex.create_view(&wgpu::TextureViewDescriptor {
220 label: Some(&format!("blur_region_dst_mip_{}", mip - 1)),
221 base_mip_level: mip - 1,
222 mip_level_count: Some(1),
223 ..Default::default()
224 })
225 })
226 .clone()
227 };
228
229 let w = (rw >> (mip - 1)).max(1);
230 let h = (rh >> (mip - 1)).max(1);
231 let kernel = mip as f32;
232
233 let uniform_data: [f32; 8] =
234 [w as f32, h as f32, mip as f32, kernel, 0.0, 0.0, 0.0, 0.0];
235 ctx.queue
236 .write_buffer(kawase_uniform, 0, bytemuck::cast_slice(&uniform_data));
237
238 let src_bg = ctx.get_or_create_bind_group(
239 (self.output_id, mip, true),
240 &ctx.renderer.kawase_bind_group_layout,
241 &[
242 wgpu::BindGroupEntry {
243 binding: 0,
244 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
245 buffer: kawase_uniform,
246 offset: 0,
247 size: wgpu::BufferSize::new(32),
248 }),
249 },
250 wgpu::BindGroupEntry {
251 binding: 1,
252 resource: wgpu::BindingResource::TextureView(&src_view),
253 },
254 wgpu::BindGroupEntry {
255 binding: 2,
256 resource: wgpu::BindingResource::Sampler(&ctx.renderer.sampler),
257 },
258 ],
259 Some(&format!("blur_region_kawase_up_bg_{}", mip)),
260 );
261
262 let mut pass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
264 label: Some(&format!("Backdrop Region Blur Up {}", mip)),
265 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
266 view: &dst_view,
267 resolve_target: None,
268 ops: wgpu::Operations {
269 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
270 store: wgpu::StoreOp::Store,
271 },
272 depth_slice: None,
273 })],
274 ..Default::default()
275 });
276 pass.set_viewport(0.0, 0.0, w as f32, h as f32, 0.0, 1.0);
277 pass.set_pipeline(&ctx.renderer.kawase_up_pipeline);
278 pass.set_bind_group(0, &src_bg, &[]);
279 pass.draw(0..3, 0..1);
280 }
281 }
282 }
283}