gardesk/garchomp / edf0251

Browse files

wire blur pipeline into render loop

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
edf0251f8cc77145606e858a9650cbd6227f3f26
Parents
4695dac
Tree
58064c6

1 changed file

StatusFile+-
M garchomp/src/render/renderer.rs 260 0
garchomp/src/render/renderer.rsmodified
@@ -50,6 +50,49 @@ impl Default for BlurConfig {
5050
     }
5151
 }
5252
 
53
+/// Intermediate render target for multi-pass rendering.
54
+struct IntermediateTarget {
55
+    texture: wgpu::Texture,
56
+    view: wgpu::TextureView,
57
+    width: u32,
58
+    height: u32,
59
+}
60
+
61
+impl IntermediateTarget {
62
+    fn new(device: &wgpu::Device, width: u32, height: u32, format: wgpu::TextureFormat) -> Self {
63
+        let texture = device.create_texture(&wgpu::TextureDescriptor {
64
+            label: Some("intermediate_render_target"),
65
+            size: wgpu::Extent3d {
66
+                width,
67
+                height,
68
+                depth_or_array_layers: 1,
69
+            },
70
+            mip_level_count: 1,
71
+            sample_count: 1,
72
+            dimension: wgpu::TextureDimension::D2,
73
+            format,
74
+            usage: wgpu::TextureUsages::RENDER_ATTACHMENT
75
+                | wgpu::TextureUsages::TEXTURE_BINDING
76
+                | wgpu::TextureUsages::COPY_SRC,
77
+            view_formats: &[],
78
+        });
79
+        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
80
+
81
+        Self {
82
+            texture,
83
+            view,
84
+            width,
85
+            height,
86
+        }
87
+    }
88
+
89
+    fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32, format: wgpu::TextureFormat) {
90
+        if self.width != width || self.height != height {
91
+            *self = Self::new(device, width, height, format);
92
+        }
93
+    }
94
+}
95
+
5396
 /// The main renderer for the compositor.
5497
 pub struct Renderer {
5598
     pub gpu: GpuContext,
@@ -64,6 +107,8 @@ pub struct Renderer {
64107
     test_texture: Option<WindowRenderData>,
65108
     // Bind groups for windows (keyed by window ID)
66109
     window_bind_groups: HashMap<u32, wgpu::BindGroup>,
110
+    // Intermediate render target for multi-pass rendering (blur support)
111
+    intermediate_texture: Option<IntermediateTarget>,
67112
 }
68113
 
69114
 impl Renderer {
@@ -102,6 +147,7 @@ impl Renderer {
102147
             },
103148
             test_texture: Some(test_texture),
104149
             window_bind_groups: HashMap::new(),
150
+            intermediate_texture: None,
105151
         })
106152
     }
107153
 
@@ -184,6 +230,8 @@ impl Renderer {
184230
     /// Resize the render surface.
185231
     pub fn resize(&mut self, width: u32, height: u32) {
186232
         self.gpu.resize(width, height);
233
+        // Invalidate intermediate texture so it gets recreated at new size
234
+        self.intermediate_texture = None;
187235
     }
188236
 
189237
     /// Set the clear color (background).
@@ -262,6 +310,19 @@ impl Renderer {
262310
             }
263311
         }
264312
 
313
+        // Check if any window needs blur
314
+        let needs_blur = self.blur_config.enabled
315
+            && windows.iter().any(|w| w.blur_behind);
316
+
317
+        if needs_blur {
318
+            self.render_windows_with_blur(windows)
319
+        } else {
320
+            self.render_windows_simple(windows)
321
+        }
322
+    }
323
+
324
+    /// Simple render path when no blur is needed.
325
+    fn render_windows_simple(&mut self, windows: &[WindowRenderInfo]) -> Result<(), GpuError> {
265326
         let frame = self.gpu.begin_frame()?;
266327
         let view = frame.texture.create_view(&wgpu::TextureViewDescriptor::default());
267328
 
@@ -328,6 +389,205 @@ impl Renderer {
328389
         Ok(())
329390
     }
330391
 
392
+    /// Render path with blur support for transparent windows.
393
+    fn render_windows_with_blur(&mut self, windows: &[WindowRenderInfo]) -> Result<(), GpuError> {
394
+        let (vw, vh) = self.gpu.dimensions();
395
+        let format = self.gpu.format();
396
+
397
+        // Ensure intermediate texture exists and is correct size
398
+        if self.intermediate_texture.is_none() {
399
+            self.intermediate_texture = Some(IntermediateTarget::new(
400
+                &self.gpu.device,
401
+                vw,
402
+                vh,
403
+                format,
404
+            ));
405
+        } else if let Some(ref mut it) = self.intermediate_texture {
406
+            it.resize(&self.gpu.device, vw, vh, format);
407
+        }
408
+
409
+        // Get frame surface
410
+        let frame = self.gpu.begin_frame()?;
411
+        let surface_view = frame.texture.create_view(&wgpu::TextureViewDescriptor::default());
412
+
413
+        let mut encoder = self.gpu.create_encoder();
414
+
415
+        // Get intermediate target view (we need to reborrow after encoder creation)
416
+        let intermediate_view = &self.intermediate_texture.as_ref().unwrap().view;
417
+
418
+        // Phase 1: Render shadows and non-blur windows to intermediate
419
+        {
420
+            let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
421
+                label: Some("composite_phase1"),
422
+                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
423
+                    view: intermediate_view,
424
+                    resolve_target: None,
425
+                    ops: wgpu::Operations {
426
+                        load: wgpu::LoadOp::Clear(self.clear_color),
427
+                        store: wgpu::StoreOp::Store,
428
+                    },
429
+                })],
430
+                depth_stencil_attachment: None,
431
+                timestamp_writes: None,
432
+                occlusion_query_set: None,
433
+            });
434
+
435
+            // Render shadows for all windows
436
+            for win in windows {
437
+                if win.shadow_enabled {
438
+                    self.shadow_pipeline.update_uniforms(
439
+                        &self.gpu.queue,
440
+                        win.x as f32,
441
+                        win.y as f32,
442
+                        win.width as f32,
443
+                        win.height as f32,
444
+                        vw as f32,
445
+                        vh as f32,
446
+                        win.corner_radius,
447
+                        &self.shadow_config,
448
+                    );
449
+                    self.shadow_pipeline.render(&mut render_pass);
450
+                }
451
+            }
452
+
453
+            // Render windows that don't need blur
454
+            for win in windows {
455
+                if !win.blur_behind {
456
+                    if let Some(bind_group) = self.window_bind_groups.get(&win.id) {
457
+                        self.pipeline.update_uniforms(
458
+                            &self.gpu.queue,
459
+                            win.x as f32,
460
+                            win.y as f32,
461
+                            win.width as f32,
462
+                            win.height as f32,
463
+                            vw as f32,
464
+                            vh as f32,
465
+                            win.opacity,
466
+                            win.corner_radius,
467
+                        );
468
+                        self.pipeline.render(&mut render_pass, bind_group);
469
+                    }
470
+                }
471
+            }
472
+        }
473
+        // Render pass ends here, intermediate texture now contains background
474
+
475
+        // Phase 2: Apply blur and render blur windows
476
+        // Run blur on the intermediate texture
477
+        let blur_iterations = self.blur_config.iterations;
478
+        let blur_strength = self.blur_config.strength;
479
+
480
+        let blurred_view = self.blur_pipeline.blur(
481
+            &self.gpu.device,
482
+            &self.gpu.queue,
483
+            &mut encoder,
484
+            intermediate_view,
485
+            vw,
486
+            vh,
487
+            blur_iterations,
488
+            blur_strength,
489
+        );
490
+
491
+        // Create bind group for blurred texture (if available) before render pass
492
+        let blur_bind_group = blurred_view.map(|view| {
493
+            self.pipeline.create_bind_group(&self.gpu.device, view)
494
+        });
495
+
496
+        // Phase 3: Final composition to surface
497
+        // We need to composite: background + blurred regions + blur windows
498
+        {
499
+            let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
500
+                label: Some("composite_final"),
501
+                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
502
+                    view: &surface_view,
503
+                    resolve_target: None,
504
+                    ops: wgpu::Operations {
505
+                        load: wgpu::LoadOp::Clear(self.clear_color),
506
+                        store: wgpu::StoreOp::Store,
507
+                    },
508
+                })],
509
+                depth_stencil_attachment: None,
510
+                timestamp_writes: None,
511
+                occlusion_query_set: None,
512
+            });
513
+
514
+            // Re-render shadows
515
+            for win in windows {
516
+                if win.shadow_enabled {
517
+                    self.shadow_pipeline.update_uniforms(
518
+                        &self.gpu.queue,
519
+                        win.x as f32,
520
+                        win.y as f32,
521
+                        win.width as f32,
522
+                        win.height as f32,
523
+                        vw as f32,
524
+                        vh as f32,
525
+                        win.corner_radius,
526
+                        &self.shadow_config,
527
+                    );
528
+                    self.shadow_pipeline.render(&mut render_pass);
529
+                }
530
+            }
531
+
532
+            // Render all windows, using blurred background for blur_behind windows
533
+            for win in windows {
534
+                if win.blur_behind {
535
+                    // First draw blurred background in window region
536
+                    if let Some(ref bg) = blur_bind_group {
537
+                        // Draw blurred background in window region
538
+                        self.pipeline.update_uniforms(
539
+                            &self.gpu.queue,
540
+                            win.x as f32,
541
+                            win.y as f32,
542
+                            win.width as f32,
543
+                            win.height as f32,
544
+                            vw as f32,
545
+                            vh as f32,
546
+                            1.0, // Opaque blur background
547
+                            win.corner_radius,
548
+                        );
549
+                        self.pipeline.render(&mut render_pass, bg);
550
+                    }
551
+
552
+                    // Then draw the transparent window on top
553
+                    if let Some(bind_group) = self.window_bind_groups.get(&win.id) {
554
+                        self.pipeline.update_uniforms(
555
+                            &self.gpu.queue,
556
+                            win.x as f32,
557
+                            win.y as f32,
558
+                            win.width as f32,
559
+                            win.height as f32,
560
+                            vw as f32,
561
+                            vh as f32,
562
+                            win.opacity,
563
+                            win.corner_radius,
564
+                        );
565
+                        self.pipeline.render(&mut render_pass, bind_group);
566
+                    }
567
+                } else {
568
+                    // Non-blur window, render normally
569
+                    if let Some(bind_group) = self.window_bind_groups.get(&win.id) {
570
+                        self.pipeline.update_uniforms(
571
+                            &self.gpu.queue,
572
+                            win.x as f32,
573
+                            win.y as f32,
574
+                            win.width as f32,
575
+                            win.height as f32,
576
+                            vw as f32,
577
+                            vh as f32,
578
+                            win.opacity,
579
+                            win.corner_radius,
580
+                        );
581
+                        self.pipeline.render(&mut render_pass, bind_group);
582
+                    }
583
+                }
584
+            }
585
+        }
586
+
587
+        self.gpu.end_frame(encoder, frame);
588
+        Ok(())
589
+    }
590
+
331591
     /// Render a frame - currently renders the test pattern.
332592
     pub fn render(&mut self) -> Result<(), GpuError> {
333593
         // For now, just render the test pattern