optimize atlas pruning
authoralfadur
Tue, 02 Apr 2019 15:53:09 +0300
changeset 14748 731c8406bff0
parent 14747 2e8213c0951f
child 14749 f5dadf2b7d03
optimize atlas pruning
rust/lib-hedgewars-engine/src/render/atlas.rs
--- a/rust/lib-hedgewars-engine/src/render/atlas.rs	Tue Apr 02 14:51:55 2019 +0200
+++ b/rust/lib-hedgewars-engine/src/render/atlas.rs	Tue Apr 02 15:53:09 2019 +0300
@@ -67,6 +67,7 @@
     size: Size,
     free_rects: Vec<Rect>,
     used_rects: Vec<Rect>,
+    splits: Vec<Rect>,
 }
 
 impl Atlas {
@@ -75,6 +76,7 @@
             size,
             free_rects: vec![Rect::at_origin(size)],
             used_rects: vec![],
+            splits: vec![],
         }
     }
 
@@ -114,30 +116,23 @@
         }
     }
 
-    fn prune(&mut self) {
-        self.free_rects = self
-            .free_rects
-            .iter()
-            .filter(|inner| {
-                self.free_rects
-                    .iter()
-                    .all(|outer| outer == *inner || !outer.contains_rect(inner))
-            })
-            .cloned()
-            .collect();
-    }
-
     fn split_insert(&mut self, rect: Rect) {
-        let mut splits = vec![];
+        let mut splits = std::mem::replace(&mut self.splits, vec![]);
+        let mut buffer = [Rect::EMPTY; 4];
 
         for i in (0..self.free_rects.len()).rev() {
-            if split_rect(self.free_rects[i], rect, &mut splits) {
+            if let Some(count) = split_rect(self.free_rects[i], rect, &mut splits, &mut buffer) {
                 self.free_rects.swap_remove(i as usize);
+                splits.extend_from_slice(&buffer[0..count]);
             }
         }
 
-        self.free_rects.extend(splits);
-        self.prune();
+        filter_swap_remove(&mut splits, |s| {
+            self.free_rects.iter().any(|r| r.contains_rect(s))
+        });
+        self.free_rects.extend(splits.drain(..));
+        std::mem::replace(&mut self.splits, splits);
+
         self.used_rects.push(rect);
     }
 
@@ -220,27 +215,86 @@
     }
 }
 
-fn split_rect(free_rect: Rect, rect: Rect, output: &mut Vec<Rect>) -> bool {
+#[inline]
+fn filter_swap_remove<T, F>(vec: &mut Vec<T>, predicate: F)
+where
+    F: Fn(&T) -> bool,
+{
+    let mut i = 0;
+    while i < vec.len() {
+        if predicate(&vec[i]) {
+            vec.swap_remove(i);
+        } else {
+            i += 1;
+        }
+    }
+}
+
+#[inline]
+fn prune_push(
+    previous_splits: &mut Vec<Rect>,
+    buffer: &mut [Rect; 4],
+    buffer_size: &mut usize,
+    rect: Rect,
+) {
+    if !previous_splits.iter().any(|r| r.contains_rect(&rect)) {
+        filter_swap_remove(previous_splits, |s| rect.contains_rect(s));
+        buffer[*buffer_size] = rect;
+        *buffer_size += 1;
+    }
+}
+
+fn split_rect(
+    free_rect: Rect,
+    rect: Rect,
+    previous_splits: &mut Vec<Rect>,
+    buffer: &mut [Rect; 4],
+) -> Option<usize> {
+    let mut buffer_size = 0usize;
     let split = free_rect.intersects(&rect);
     if split {
         if rect.left() > free_rect.left() {
             let trim = free_rect.right() - rect.left() + 1;
-            output.push(free_rect.with_margins(0, -trim, 0, 0))
+            prune_push(
+                previous_splits,
+                buffer,
+                &mut buffer_size,
+                free_rect.with_margins(0, -trim, 0, 0),
+            );
         }
         if rect.right() < free_rect.right() {
             let trim = rect.right() - free_rect.left() + 1;
-            output.push(free_rect.with_margins(-trim, 0, 0, 0))
+            prune_push(
+                previous_splits,
+                buffer,
+                &mut buffer_size,
+                free_rect.with_margins(-trim, 0, 0, 0),
+            );
         }
         if rect.top() > free_rect.top() {
             let trim = free_rect.bottom() - rect.top() + 1;
-            output.push(free_rect.with_margins(0, 0, 0, -trim));
+            prune_push(
+                previous_splits,
+                buffer,
+                &mut buffer_size,
+                free_rect.with_margins(0, 0, 0, -trim),
+            );;
         }
         if rect.bottom() < free_rect.bottom() {
             let trim = rect.bottom() - free_rect.top() + 1;
-            output.push(free_rect.with_margins(0, 0, -trim, 0));
+            prune_push(
+                previous_splits,
+                buffer,
+                &mut buffer_size,
+                free_rect.with_margins(0, 0, -trim, 0),
+            );;
         }
     }
-    split
+    if split {
+        Some(buffer_size)
+    } else {
+        None
+    }
 }
 
 #[cfg(test)]