Repository: iced-rs/iced (language: rust)

Issue:
Layer rendering breaks if one is not drawn at any point
### Is your issue REALLY a bug?

- [X] My issue is indeed a bug!
- [X] I am not crazy! I will not fill out this form just to ask a question or request a feature. Pinky promise.

### Is there an existing issue for this?

- [X] I have searched the existing issues.

### Is this issue related to iced?

- [X] My hardware is compatible and my graphics drivers are up-to-date.

### What happened?

If you have a layer that is not drawn either because it is fully outside of the application window, or because its snapped bounds have an area of zero, this breaks subsequent layer draws. This is because at https://github.com/iced-rs/iced/blob/f2c9b6b2ffc50d67d9789e77cb55eeb2a0ebe470/wgpu/src/lib.rs#L271-L275 and https://github.com/iced-rs/iced/blob/f2c9b6b2ffc50d67d9789e77cb55eeb2a0ebe470/wgpu/src/lib.rs#L277-L279 rendering for those layers is skipped without updating the `quad_layer`, `mesh_layer` and `text_layer` indices.

Here is a video of this occuring in a real iced app:

https://github.com/user-attachments/assets/f0c0a6f2-c44f-4dea-88dc-ea8a0c4a9f31

and the relevant lines in the source code of this app: https://github.com/generic-daw/generic-daw/blob/b5593615b46625cfde0f194d16582f15cc672d7a/src/generic_front/widget/track.rs#L56-L59

### What is the expected behavior?

Layer rendering doesn't break when a layer is not visible.

### Version

crates.io release

### Operating System

Linux

### Do you have any log output?

_No response_


Discussion / hints:

Fix patch:
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index d79f0dc85f..e6e435afb0 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -268,13 +268,28 @@ impl Renderer {
         let scale = Transformation::scale(scale_factor);
 
         for layer in self.layers.iter() {
-            let Some(physical_bounds) =
-                physical_bounds.intersection(&(layer.bounds * scale))
+            let Some(scissor_rect) = physical_bounds
+                .intersection(&(layer.bounds * scale))
+                .and_then(Rectangle::snap)
             else {
-                continue;
-            };
+                if !layer.quads.is_empty() {
+                    quad_layer += 1;
+                }
+
+                if !layer.triangles.is_empty() {
+                    mesh_layer +=
+                        triangle::Pipeline::layer_count(&layer.triangles);
+                }
+
+                if !layer.text.is_empty() {
+                    text_layer += text::Pipeline::layer_count(&layer.text);
+                }
+
+                #[cfg(any(feature = "svg", feature = "image"))]
+                if !layer.images.is_empty() {
+                    image_layer += 1;
+                }
 
-            let Some(scissor_rect) = physical_bounds.snap() else {
                 continue;
             };
 
diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index 591bc0b704..d7e2e2aeae 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -302,6 +302,13 @@ impl Pipeline {
         }
     }
 
+    pub fn layer_count(batch: &Batch) -> usize {
+        batch
+            .iter()
+            .filter(|item| matches!(item, Item::Group { .. }))
+            .count()
+    }
+
     pub fn prepare(
         &mut self,
         device: &wgpu::Device,
diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs
index ab88be3bff..ac0b2215ae 100644
--- a/wgpu/src/triangle.rs
+++ b/wgpu/src/triangle.rs
@@ -177,6 +177,13 @@ impl Pipeline {
         }
     }
 
+    pub fn layer_count(items: &[Item]) -> usize {
+        items
+            .iter()
+            .filter(|item| matches!(item, Item::Group { .. }))
+            .count()
+    }
+
     pub fn prepare(
         &mut self,
         device: &wgpu::Device,


Test patch: