gardesk/garchomp / 49a66f2

Browse files

fix per-window shadow rendering with separate bind groups

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
49a66f2c07ee1f56593bd98b372111a93e127cd7
Parents
113196c
Tree
087e32a

2 changed files

StatusFile+-
M garchomp/src/render/renderer.rs 120 83
M garchomp/src/render/shadow.rs 38 37
garchomp/src/render/renderer.rsmodified
@@ -29,6 +29,10 @@ pub struct WindowRenderInfo {
2929
     pub blur_behind: bool,
3030
     /// Whether this window is currently focused.
3131
     pub focused: bool,
32
+    /// Scale factor from Lua animation (x, y). Default: (1.0, 1.0).
33
+    pub scale: (f32, f32),
34
+    /// Position offset from Lua animation (x, y). Default: (0.0, 0.0).
35
+    pub offset: (f32, f32),
3236
 }
3337
 
3438
 /// Blur configuration.
@@ -111,6 +115,8 @@ pub struct Renderer {
111115
     test_uniform_buffer: wgpu::Buffer,
112116
     // Bind groups for windows (keyed by window ID) with uniform buffer and actual texture dimensions
113117
     window_bind_groups: HashMap<u32, (wgpu::BindGroup, wgpu::Buffer, u32, u32)>,
118
+    // Shadow bind groups for windows (keyed by window ID) with uniform buffer
119
+    shadow_bind_groups: HashMap<u32, (wgpu::BindGroup, wgpu::Buffer)>,
114120
     // Intermediate render target for multi-pass rendering (blur support)
115121
     intermediate_texture: Option<IntermediateTarget>,
116122
     // HDR render target and tonemapping (optional)
@@ -173,6 +179,7 @@ impl Renderer {
173179
             test_texture: Some(test_texture),
174180
             test_uniform_buffer,
175181
             window_bind_groups: HashMap::new(),
182
+            shadow_bind_groups: HashMap::new(),
176183
             intermediate_texture: None,
177184
             hdr_target: None,
178185
             tonemap_pipeline: None,
@@ -344,16 +351,16 @@ impl Renderer {
344351
                     Ok(tex) => {
345352
                         tracing::trace!("Texture updated for window {:#x} (actual size {}x{})", win.id, tex.width, tex.height);
346353
                         // Create or update bind group for this window with its own uniform buffer
347
-                        // Only create new uniform buffer if window doesn't have one yet
348
-                        let uniform_buffer = if let Some((_, existing_buffer, _, _)) = self.window_bind_groups.get(&win.id) {
349
-                            // Reuse existing buffer (just update bind group with new texture view)
350
-                            // This is a bit wasteful but simpler - we clone the buffer reference
351
-                            self.pipeline.create_uniform_buffer(&self.gpu.device)
352
-                        } else {
353
-                            self.pipeline.create_uniform_buffer(&self.gpu.device)
354
-                        };
354
+                        let uniform_buffer = self.pipeline.create_uniform_buffer(&self.gpu.device);
355355
                         let bind_group = self.pipeline.create_bind_group(&self.gpu.device, &tex.view, &uniform_buffer);
356356
                         self.window_bind_groups.insert(win.id, (bind_group, uniform_buffer, tex.width, tex.height));
357
+
358
+                        // Create shadow bind group for windows that need shadows
359
+                        if win.shadow_enabled && !self.shadow_bind_groups.contains_key(&win.id) {
360
+                            let shadow_buffer = self.shadow_pipeline.create_uniform_buffer(&self.gpu.device);
361
+                            let shadow_bind = self.shadow_pipeline.create_bind_group(&self.gpu.device, &shadow_buffer);
362
+                            self.shadow_bind_groups.insert(win.id, (shadow_bind, shadow_buffer));
363
+                        }
357364
                     }
358365
                     Err(e) => {
359366
                         tracing::warn!("Failed to update texture for window {:#x}: {}", win.id, e);
@@ -409,41 +416,56 @@ impl Renderer {
409416
             // First pass: render shadows for all windows (back to front)
410417
             for win in windows {
411418
                 if win.shadow_enabled {
412
-                    // Use actual texture dimensions for shadow sizing
413
-                    let (w, h) = if let Some((_, _, tex_w, tex_h)) = self.window_bind_groups.get(&win.id) {
414
-                        (*tex_w as f32, *tex_h as f32)
415
-                    } else {
416
-                        (win.width as f32, win.height as f32)
417
-                    };
418
-                    self.shadow_pipeline.update_uniforms(
419
-                        &self.gpu.queue,
420
-                        win.x as f32,
421
-                        win.y as f32,
422
-                        w,
423
-                        h,
424
-                        vw as f32,
425
-                        vh as f32,
426
-                        win.corner_radius,
427
-                        &self.shadow_config,
428
-                    );
429
-                    self.shadow_pipeline.render(&mut render_pass);
419
+                    if let Some((shadow_bind, shadow_buffer)) = self.shadow_bind_groups.get(&win.id) {
420
+                        // Use actual texture dimensions for shadow sizing
421
+                        let (base_w, base_h) = if let Some((_, _, tex_w, tex_h)) = self.window_bind_groups.get(&win.id) {
422
+                            (*tex_w as f32, *tex_h as f32)
423
+                        } else {
424
+                            (win.width as f32, win.height as f32)
425
+                        };
426
+                        // Apply Lua animation transforms
427
+                        let x = win.x as f32 + win.offset.0;
428
+                        let y = win.y as f32 + win.offset.1;
429
+                        let w = base_w * win.scale.0;
430
+                        let h = base_h * win.scale.1;
431
+
432
+                        self.shadow_pipeline.update_uniforms(
433
+                            &self.gpu.queue,
434
+                            shadow_buffer,
435
+                            x,
436
+                            y,
437
+                            w,
438
+                            h,
439
+                            vw as f32,
440
+                            vh as f32,
441
+                            win.corner_radius,
442
+                            &self.shadow_config,
443
+                        );
444
+                        self.shadow_pipeline.render(&mut render_pass, shadow_bind);
445
+                    }
430446
                 }
431447
             }
432448
 
433449
             // Second pass: render windows (back to front)
434450
             for win in windows.iter() {
435451
                 if let Some((bind_group, uniform_buffer, tex_w, tex_h)) = self.window_bind_groups.get(&win.id) {
452
+                    // Apply Lua animation transforms: offset position and scale size
453
+                    let x = win.x as f32 + win.offset.0;
454
+                    let y = win.y as f32 + win.offset.1;
455
+                    let w = *tex_w as f32 * win.scale.0;
456
+                    let h = *tex_h as f32 * win.scale.1;
457
+
436458
                     tracing::debug!(
437
-                        "Rendering window {:#x} at ({},{}) size {}x{} opacity={}",
438
-                        win.id, win.x, win.y, tex_w, tex_h, win.opacity
459
+                        "Rendering window {:#x} at ({},{}) size {}x{} opacity={} scale=({:.2},{:.2})",
460
+                        win.id, x, y, w, h, win.opacity, win.scale.0, win.scale.1
439461
                     );
440462
                     self.pipeline.update_uniforms(
441463
                         &self.gpu.queue,
442464
                         uniform_buffer,
443
-                        win.x as f32,
444
-                        win.y as f32,
445
-                        *tex_w as f32,
446
-                        *tex_h as f32,
465
+                        x,
466
+                        y,
467
+                        w,
468
+                        h,
447469
                         vw as f32,
448470
                         vh as f32,
449471
                         win.opacity,
@@ -507,23 +529,26 @@ impl Renderer {
507529
             // Render shadows for all windows
508530
             for win in windows {
509531
                 if win.shadow_enabled {
510
-                    let (w, h) = if let Some((_, _, tex_w, tex_h)) = self.window_bind_groups.get(&win.id) {
511
-                        (*tex_w as f32, *tex_h as f32)
512
-                    } else {
513
-                        (win.width as f32, win.height as f32)
514
-                    };
515
-                    self.shadow_pipeline.update_uniforms(
516
-                        &self.gpu.queue,
517
-                        win.x as f32,
518
-                        win.y as f32,
519
-                        w,
520
-                        h,
521
-                        vw as f32,
522
-                        vh as f32,
523
-                        win.corner_radius,
524
-                        &self.shadow_config,
525
-                    );
526
-                    self.shadow_pipeline.render(&mut render_pass);
532
+                    if let Some((shadow_bind, shadow_buffer)) = self.shadow_bind_groups.get(&win.id) {
533
+                        let (w, h) = if let Some((_, _, tex_w, tex_h)) = self.window_bind_groups.get(&win.id) {
534
+                            (*tex_w as f32, *tex_h as f32)
535
+                        } else {
536
+                            (win.width as f32, win.height as f32)
537
+                        };
538
+                        self.shadow_pipeline.update_uniforms(
539
+                            &self.gpu.queue,
540
+                            shadow_buffer,
541
+                            win.x as f32,
542
+                            win.y as f32,
543
+                            w,
544
+                            h,
545
+                            vw as f32,
546
+                            vh as f32,
547
+                            win.corner_radius,
548
+                            &self.shadow_config,
549
+                        );
550
+                        self.shadow_pipeline.render(&mut render_pass, shadow_bind);
551
+                    }
527552
                 }
528553
             }
529554
 
@@ -595,23 +620,26 @@ impl Renderer {
595620
             // Re-render shadows
596621
             for win in windows {
597622
                 if win.shadow_enabled {
598
-                    let (w, h) = if let Some((_, _, tex_w, tex_h)) = self.window_bind_groups.get(&win.id) {
599
-                        (*tex_w as f32, *tex_h as f32)
600
-                    } else {
601
-                        (win.width as f32, win.height as f32)
602
-                    };
603
-                    self.shadow_pipeline.update_uniforms(
604
-                        &self.gpu.queue,
605
-                        win.x as f32,
606
-                        win.y as f32,
607
-                        w,
608
-                        h,
609
-                        vw as f32,
610
-                        vh as f32,
611
-                        win.corner_radius,
612
-                        &self.shadow_config,
613
-                    );
614
-                    self.shadow_pipeline.render(&mut render_pass);
623
+                    if let Some((shadow_bind, shadow_buffer)) = self.shadow_bind_groups.get(&win.id) {
624
+                        let (w, h) = if let Some((_, _, tex_w, tex_h)) = self.window_bind_groups.get(&win.id) {
625
+                            (*tex_w as f32, *tex_h as f32)
626
+                        } else {
627
+                            (win.width as f32, win.height as f32)
628
+                        };
629
+                        self.shadow_pipeline.update_uniforms(
630
+                            &self.gpu.queue,
631
+                            shadow_buffer,
632
+                            win.x as f32,
633
+                            win.y as f32,
634
+                            w,
635
+                            h,
636
+                            vw as f32,
637
+                            vh as f32,
638
+                            win.corner_radius,
639
+                            &self.shadow_config,
640
+                        );
641
+                        self.shadow_pipeline.render(&mut render_pass, shadow_bind);
642
+                    }
615643
                 }
616644
             }
617645
 
@@ -693,6 +721,7 @@ impl Renderer {
693721
     pub fn remove_window(&mut self, window_id: u32) {
694722
         self.texture_manager.remove_texture(window_id);
695723
         self.window_bind_groups.remove(&window_id);
724
+        self.shadow_bind_groups.remove(&window_id);
696725
     }
697726
 
698727
     /// Poll the GPU device and sync display.
@@ -807,12 +836,17 @@ impl Renderer {
807836
         // Create bind groups for HDR pipeline (must use HDR pipeline's bind group layout)
808837
         // Store both bind group and uniform buffer per window
809838
         let mut hdr_bind_groups: HashMap<u32, (wgpu::BindGroup, wgpu::Buffer)> = HashMap::new();
839
+        let mut hdr_shadow_bind_groups: HashMap<u32, (wgpu::BindGroup, wgpu::Buffer)> = HashMap::new();
810840
         for (id, (_, _, _, _)) in &self.window_bind_groups {
811841
             if let Some(tex) = self.texture_manager.get_texture(*id) {
812842
                 let uniform_buffer = hdr_pipeline.create_uniform_buffer(&self.gpu.device);
813843
                 let bind_group = hdr_pipeline.create_bind_group(&self.gpu.device, &tex.view, &uniform_buffer);
814844
                 hdr_bind_groups.insert(*id, (bind_group, uniform_buffer));
815845
             }
846
+            // Create HDR shadow bind group for this window
847
+            let shadow_buffer = hdr_shadow_pipeline.create_uniform_buffer(&self.gpu.device);
848
+            let shadow_bind = hdr_shadow_pipeline.create_bind_group(&self.gpu.device, &shadow_buffer);
849
+            hdr_shadow_bind_groups.insert(*id, (shadow_bind, shadow_buffer));
816850
         }
817851
 
818852
         let frame = self.gpu.begin_frame()?;
@@ -848,23 +882,26 @@ impl Renderer {
848882
             // Render shadows using HDR shadow pipeline
849883
             for win in windows {
850884
                 if win.shadow_enabled {
851
-                    let (w, h) = if let Some((_, _, tex_w, tex_h)) = self.window_bind_groups.get(&win.id) {
852
-                        (*tex_w as f32, *tex_h as f32)
853
-                    } else {
854
-                        (win.width as f32, win.height as f32)
855
-                    };
856
-                    hdr_shadow_pipeline.update_uniforms(
857
-                        &self.gpu.queue,
858
-                        win.x as f32,
859
-                        win.y as f32,
860
-                        w,
861
-                        h,
862
-                        vw as f32,
863
-                        vh as f32,
864
-                        win.corner_radius,
865
-                        &self.shadow_config,
866
-                    );
867
-                    hdr_shadow_pipeline.render(&mut render_pass);
885
+                    if let Some((shadow_bind, shadow_buffer)) = hdr_shadow_bind_groups.get(&win.id) {
886
+                        let (w, h) = if let Some((_, _, tex_w, tex_h)) = self.window_bind_groups.get(&win.id) {
887
+                            (*tex_w as f32, *tex_h as f32)
888
+                        } else {
889
+                            (win.width as f32, win.height as f32)
890
+                        };
891
+                        hdr_shadow_pipeline.update_uniforms(
892
+                            &self.gpu.queue,
893
+                            shadow_buffer,
894
+                            win.x as f32,
895
+                            win.y as f32,
896
+                            w,
897
+                            h,
898
+                            vw as f32,
899
+                            vh as f32,
900
+                            win.corner_radius,
901
+                            &self.shadow_config,
902
+                        );
903
+                        hdr_shadow_pipeline.render(&mut render_pass, shadow_bind);
904
+                    }
868905
                 }
869906
             }
870907
 
garchomp/src/render/shadow.rsmodified
@@ -69,8 +69,6 @@ pub struct ShadowUniforms {
6969
 pub struct ShadowPipeline {
7070
     pipeline: wgpu::RenderPipeline,
7171
     bind_group_layout: wgpu::BindGroupLayout,
72
-    uniform_buffer: wgpu::Buffer,
73
-    bind_group: wgpu::BindGroup,
7472
     vertex_buffer: wgpu::Buffer,
7573
     index_buffer: wgpu::Buffer,
7674
 }
@@ -141,36 +139,6 @@ impl ShadowPipeline {
141139
             cache: None,
142140
         });
143141
 
144
-        // Create uniform buffer
145
-        let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
146
-            label: Some("shadow_uniform_buffer"),
147
-            contents: bytemuck::cast_slice(&[ShadowUniforms {
148
-                transform: [0.0, 0.0, 100.0, 100.0],
149
-                viewport: [1920.0, 1080.0],
150
-                _pad0: [0.0; 2],
151
-                color: [0.0, 0.0, 0.0],
152
-                opacity: 0.5,
153
-                window_size: [100.0, 100.0],
154
-                spread: 15.0,
155
-                blur_radius: 12.0,
156
-                corner_radius: 0.0,
157
-                _pad1: 0.0,
158
-                offset: [0.0, 5.0],
159
-                _pad2: [0.0; 4],
160
-            }]),
161
-            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
162
-        });
163
-
164
-        // Create bind group
165
-        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
166
-            label: Some("shadow_bind_group"),
167
-            layout: &bind_group_layout,
168
-            entries: &[wgpu::BindGroupEntry {
169
-                binding: 0,
170
-                resource: uniform_buffer.as_entire_binding(),
171
-            }],
172
-        });
173
-
174142
         // Create vertex buffer (unit quad)
175143
         let vertices = [
176144
             Vertex {
@@ -207,17 +175,50 @@ impl ShadowPipeline {
207175
         Self {
208176
             pipeline,
209177
             bind_group_layout,
210
-            uniform_buffer,
211
-            bind_group,
212178
             vertex_buffer,
213179
             index_buffer,
214180
         }
215181
     }
216182
 
183
+    /// Create a uniform buffer for a window's shadow.
184
+    pub fn create_uniform_buffer(&self, device: &wgpu::Device) -> wgpu::Buffer {
185
+        device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
186
+            label: Some("shadow_uniform_buffer"),
187
+            contents: bytemuck::cast_slice(&[ShadowUniforms {
188
+                transform: [0.0, 0.0, 100.0, 100.0],
189
+                viewport: [1920.0, 1080.0],
190
+                _pad0: [0.0; 2],
191
+                color: [0.0, 0.0, 0.0],
192
+                opacity: 0.5,
193
+                window_size: [100.0, 100.0],
194
+                spread: 15.0,
195
+                blur_radius: 12.0,
196
+                corner_radius: 0.0,
197
+                _pad1: 0.0,
198
+                offset: [0.0, 5.0],
199
+                _pad2: [0.0; 4],
200
+            }]),
201
+            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
202
+        })
203
+    }
204
+
205
+    /// Create a bind group for a window's shadow.
206
+    pub fn create_bind_group(&self, device: &wgpu::Device, uniform_buffer: &wgpu::Buffer) -> wgpu::BindGroup {
207
+        device.create_bind_group(&wgpu::BindGroupDescriptor {
208
+            label: Some("shadow_bind_group"),
209
+            layout: &self.bind_group_layout,
210
+            entries: &[wgpu::BindGroupEntry {
211
+                binding: 0,
212
+                resource: uniform_buffer.as_entire_binding(),
213
+            }],
214
+        })
215
+    }
216
+
217217
     /// Update uniforms for a window shadow.
218218
     pub fn update_uniforms(
219219
         &self,
220220
         queue: &wgpu::Queue,
221
+        uniform_buffer: &wgpu::Buffer,
221222
         window_x: f32,
222223
         window_y: f32,
223224
         window_width: f32,
@@ -251,13 +252,13 @@ impl ShadowPipeline {
251252
             _pad2: [0.0; 4],
252253
         };
253254
 
254
-        queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
255
+        queue.write_buffer(uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
255256
     }
256257
 
257258
     /// Render a shadow.
258
-    pub fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
259
+    pub fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>, bind_group: &'a wgpu::BindGroup) {
259260
         render_pass.set_pipeline(&self.pipeline);
260
-        render_pass.set_bind_group(0, &self.bind_group, &[]);
261
+        render_pass.set_bind_group(0, bind_group, &[]);
261262
         render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
262263
         render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
263264
         render_pass.draw_indexed(0..6, 0, 0..1);