1use std::num::Wrapping;
2use std::u16;
3
4use crate::{AllocatorOptions, DEFAULT_OPTIONS, Allocation, AllocId, Size, Rectangle, point2, size2};
5
6const BIN_BITS: u32 = 12;
7const ITEM_BITS: u32 = 12;
8const GEN_BITS: u32 = 8;
9
10const BIN_MASK: u32 = (1 << BIN_BITS) - 1;
11const ITEM_MASK: u32 = ((1 << ITEM_BITS) - 1) << BIN_BITS;
12const GEN_MASK: u32 = ((1 << GEN_BITS) - 1) << (BIN_BITS + ITEM_BITS);
13
14const MAX_ITEMS_PER_BIN: u16 = (ITEM_MASK >> 12) as u16;
15const MAX_BIN_COUNT: usize = BIN_MASK as usize;
16const MAX_SHELF_COUNT: usize = u16::MAX as usize;
17
18#[derive(Copy, Clone, Debug, PartialEq, Eq)]
19#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
20struct BucketIndex(u16);
21
22impl BucketIndex {
23 fn to_usize(self) -> usize {
24 self.0 as usize
25 }
26
27 const INVALID: Self = BucketIndex(u16::MAX);
28}
29
30#[derive(Clone)]
31#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
32struct Shelf {
33 x: u16,
34 y: u16,
35 height: u16,
36 bucket_width: u16,
37
38 first_bucket: BucketIndex,
39}
40
41#[derive(Clone)]
42#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
43struct Bucket {
44 x: u16,
45 free_space: u16,
46
47 next: BucketIndex,
48
49 refcount: u16,
51 item_count: u16,
55 shelf: u16,
56 generation: Wrapping<u8>,
57}
58
59#[derive(Clone)]
72#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
73pub struct BucketedAtlasAllocator {
74 shelves: Vec<Shelf>,
75 buckets: Vec<Bucket>,
76 available_height: u16,
77 width: u16,
78 height: u16,
79 first_unallocated_bucket: BucketIndex,
80 flip_xy: bool,
81 alignment: Size,
82 current_column: u16,
83 column_width: u16,
84 num_columns: u16,
85 allocated_space: i32,
86}
87
88impl BucketedAtlasAllocator {
89 pub fn with_options(size: Size, options: &AllocatorOptions) -> Self {
91 assert!(size.width < u16::MAX as i32);
92 assert!(size.height < u16::MAX as i32);
93
94 let (width, height, shelf_alignment) = if options.vertical_shelves {
95 (size.height as u16, size.width as u16, options.alignment.height as u16)
96 } else {
97 (size.width as u16, size.height as u16, options.alignment.width as u16)
98 };
99
100 let mut column_width = width / (options.num_columns as u16);
101 column_width = column_width - column_width % shelf_alignment;
102
103 BucketedAtlasAllocator {
104 shelves: Vec::new(),
105 buckets: Vec::new(),
106 available_height: height,
107 width,
108 height,
109 first_unallocated_bucket: BucketIndex::INVALID,
110 flip_xy: options.vertical_shelves,
111 alignment: options.alignment,
112 current_column: 0,
113 num_columns: options.num_columns as u16,
114 column_width,
115 allocated_space: 0,
116 }
117 }
118
119 pub fn new(size: Size) -> Self {
121 Self::with_options(size, &DEFAULT_OPTIONS)
122 }
123
124 pub fn clear(&mut self) {
125 self.shelves.clear();
126 self.buckets.clear();
127 self.first_unallocated_bucket = BucketIndex::INVALID;
128 self.available_height = self.height;
129 self.current_column = 0;
130 self.allocated_space = 0;
131 }
132
133 pub fn size(&self) -> Size {
134 let (w, h) = convert_coordinates(self.flip_xy, self.width, self.height);
135 size2(w as i32, h as i32)
136 }
137
138 pub fn grow(&mut self, new_size: Size) {
139 assert!(new_size.width < u16::MAX as i32);
140 assert!(new_size.height < u16::MAX as i32);
141
142 let (new_width, new_height) = if self.flip_xy {
143 (new_size.height as u16, new_size.width as u16)
144 } else {
145 (new_size.width as u16, new_size.height as u16)
146 };
147
148 assert!(new_width >= self.width);
149 assert!(new_height >= self.height);
150
151 self.available_height += new_height - self.height;
152 self.width = new_width;
153 self.height = new_height;
154
155 if self.num_columns == 1 {
156 let additional_width = self.width - self.column_width;
158
159 let len = self.shelves.len();
160
161 for shelf_index in 0..len {
162 let shelf = &self.shelves[shelf_index];
163 let mut x = self.column_width;
164 let bucket_width = shelf.bucket_width;
165
166 let max_new_buckets = (MAX_BIN_COUNT - self.buckets.len()) as u16;
167 let mut num_buckets_to_add = additional_width / bucket_width;
168 num_buckets_to_add = num_buckets_to_add.min(max_new_buckets);
169
170 let mut bucket_next = shelf.first_bucket;
171
172 for _ in 0..num_buckets_to_add {
173 let bucket = Bucket {
174 next: bucket_next,
175 x,
176 free_space: bucket_width,
177 refcount: 0,
178 shelf: shelf_index as u16,
179 generation: Wrapping(0),
180 item_count: 0,
181 };
182
183 x += bucket_width;
184
185 let bucket_index = self.add_bucket(bucket);
186
187 bucket_next = bucket_index;
188 }
189
190 self.shelves[shelf_index].first_bucket = bucket_next;
191 }
192
193 self.column_width = self.width;
195 } else {
196 self.num_columns = self.width / self.column_width;
198 }
199 }
200
201 pub fn is_empty(&self) -> bool {
202 self.shelves.is_empty()
203 }
204
205 pub fn allocate(&mut self, mut requested_size: Size) -> Option<Allocation> {
207 if requested_size.is_empty()
208 || requested_size.width > std::u16::MAX as i32
209 || requested_size.height > std::u16::MAX as i32 {
210 return None;
211 }
212
213 adjust_size(self.alignment.width, &mut requested_size.width);
214 adjust_size(self.alignment.height, &mut requested_size.height);
215
216 if requested_size.width > self.column_width as i32 || requested_size.height > self.height as i32 {
217 return None;
218 }
219
220 let (w, h) = convert_coordinates(self.flip_xy, requested_size.width as u16, requested_size.height as u16);
221
222 let mut selected_shelf = std::usize::MAX;
223 let mut selected_bucket = BucketIndex::INVALID;
224 let mut best_waste = u16::MAX;
225
226 let can_add_shelf = (self.available_height >= h || self.current_column + 1 < self.num_columns)
227 && self.shelves.len() < MAX_SHELF_COUNT
228 && self.buckets.len() < MAX_BIN_COUNT;
229
230 'shelves: for (shelf_index, shelf) in self.shelves.iter().enumerate() {
231 if shelf.height < h || shelf.bucket_width < w {
232 continue;
233 }
234
235 let y_waste = shelf.height - h;
236 if y_waste > best_waste || (can_add_shelf && y_waste > h) {
237 continue;
238 }
239
240 let mut bucket_index = shelf.first_bucket;
241 while bucket_index != BucketIndex::INVALID {
242 let bucket = &self.buckets[bucket_index.to_usize()];
243
244 if bucket.free_space >= w && bucket.item_count < MAX_ITEMS_PER_BIN {
245 if y_waste == 0 && bucket.free_space == w {
246 selected_shelf = shelf_index;
247 selected_bucket = bucket_index;
248
249 break 'shelves;
250 }
251
252 if y_waste < best_waste {
253 best_waste = y_waste;
254 selected_shelf = shelf_index;
255 selected_bucket = bucket_index;
256 break;
257 }
258 }
259
260 bucket_index = bucket.next;
261 }
262 }
263
264 if selected_bucket == BucketIndex::INVALID {
265 if can_add_shelf {
266 selected_shelf = self.add_shelf(w, h);
267 selected_bucket = self.shelves[selected_shelf].first_bucket;
268 } else {
269 let selected = self.coalesce_shelves(w, h);
271 selected_shelf = selected.0;
272 selected_bucket = selected.1;
273 }
274 }
275
276 if selected_bucket != BucketIndex::INVALID {
277 return self.alloc_from_bucket(selected_shelf, selected_bucket, w);
278 }
279
280 return None;
281 }
282
283 pub fn deallocate(&mut self, id: AllocId) {
287 if self.deallocate_from_bucket(id) {
288 self.cleanup_shelves();
289 }
290
291 self.check()
292 }
293
294 pub fn allocated_space(&self) -> i32 {
296 self.allocated_space
297 }
298
299 pub fn free_space(&self) -> i32 {
301 (self.width as i32 * self.height as i32) - self.allocated_space
302 }
303
304 fn alloc_from_bucket(&mut self, shelf_index: usize, bucket_index: BucketIndex, width: u16) -> Option<Allocation> {
305 let shelf = &mut self.shelves[shelf_index];
306 let bucket = &mut self.buckets[bucket_index.to_usize()];
307
308 debug_assert!(bucket.free_space >= width);
309
310 let min_x = bucket.x + shelf.bucket_width - bucket.free_space;
311 let min_y = shelf.y;
312 let max_x = min_x + width;
313 let max_y = min_y + shelf.height;
314
315 let (min_x, min_y) = convert_coordinates(self.flip_xy, min_x, min_y);
316 let (max_x, max_y) = convert_coordinates(self.flip_xy, max_x, max_y);
317
318 bucket.free_space -= width;
319 bucket.refcount += 1;
320 bucket.item_count += 1;
321
322 let id = AllocId(
323 (bucket_index.0 as u32) & BIN_MASK
324 | ((bucket.item_count as u32) << 12) & ITEM_MASK
325 | (bucket.generation.0 as u32) << 24
326 );
327
328 let rectangle = Rectangle {
329 min: point2(min_x as i32, min_y as i32),
330 max: point2(max_x as i32, max_y as i32),
331 };
332
333 self.allocated_space += rectangle.size().area();
334
335 self.check();
336
337 Some(Allocation { id, rectangle })
338 }
339
340 fn add_bucket(&mut self, mut bucket: Bucket) -> BucketIndex {
341 let mut bucket_index = self.first_unallocated_bucket;
342
343 if bucket_index == BucketIndex::INVALID {
344 bucket_index = BucketIndex(self.buckets.len() as u16);
345 self.buckets.push(bucket);
346 } else {
347 let idx = bucket_index.to_usize();
348 bucket.generation = self.buckets[idx].generation + Wrapping(1);
349 self.first_unallocated_bucket = self.buckets[idx].next;
350 self.buckets[idx] = bucket;
351 }
352
353 bucket_index
354 }
355
356 fn add_shelf(&mut self, width: u16, height: u16) -> usize {
357
358 let can_add_column = self.current_column + 1 < self.num_columns;
359
360 if self.available_height != 0 && self.available_height < height && can_add_column {
361 self.add_shelf(0, self.available_height);
365 debug_assert_eq!(self.available_height, 0);
366 }
367
368 if self.available_height == 0 && can_add_column {
369 self.current_column += 1;
370 self.available_height = self.height;
371 }
372
373 let height = shelf_height(height).min(self.available_height);
374 let num_buckets = self.num_buckets(width, height);
375 let mut bucket_width = self.column_width / num_buckets;
376 bucket_width = bucket_width - (bucket_width % self.alignment.width as u16); let y = self.height - self.available_height;
378 self.available_height -= height;
379
380 let shelf_index = self.shelves.len();
381
382 let mut x = self.current_column * self.column_width;
384 let mut bucket_next = BucketIndex::INVALID;
385 for _ in 0..num_buckets {
386 let bucket = Bucket {
387 next: bucket_next,
388 x,
389 free_space: bucket_width,
390 refcount: 0,
391 shelf: shelf_index as u16,
392 generation: Wrapping(0),
393 item_count: 0,
394 };
395
396 x += bucket_width;
397
398 let bucket_index = self.add_bucket(bucket);
399
400 bucket_next = bucket_index;
401 }
402
403 self.shelves.push(Shelf {
404 x: self.current_column * self.column_width,
405 y,
406 height,
407 bucket_width,
408 first_bucket: bucket_next,
409 });
410
411 shelf_index
412 }
413
414 fn coalesce_shelves(&mut self, w: u16, h: u16) -> (usize, BucketIndex) {
424 let len = self.shelves.len();
425 let mut coalesce_range = None;
426 let mut coalesced_height = 0;
427
428 'outer: for shelf_index in 0..len {
429 if self.shelves[shelf_index].bucket_width < w {
430 continue;
431 }
432 if !self.shelf_is_empty(shelf_index) {
433 continue;
434 }
435 let shelf_x = self.shelves[shelf_index].x;
436 coalesced_height = self.shelves[shelf_index].height;
437 for i in 1..3 {
438 if self.shelves[shelf_index + i].x != shelf_x {
439 continue 'outer;
441 }
442
443 if shelf_index + i >= len {
444 break 'outer;
445 }
446
447 if !self.shelf_is_empty(shelf_index + i) {
448 continue 'outer;
449 }
450
451 coalesced_height += self.shelves[shelf_index + i].height;
452
453 if coalesced_height >= h {
454 coalesce_range = Some(shelf_index .. (shelf_index + i + 1));
455 break 'outer;
456 }
457 }
458 }
459
460 if let Some(range) = coalesce_range {
461 let y_top = self.shelves[range.start].y + coalesced_height;
462 for i in range.start + 1 .. range.end {
463 self.shelves[i].y = y_top;
464 self.shelves[i].height = 0;
465 }
466
467 let shelf_index = range.start;
468 let shelf = &mut self.shelves[shelf_index];
469 shelf.height = coalesced_height;
470
471 return (shelf_index, shelf.first_bucket);
472 }
473
474 (0, BucketIndex::INVALID)
475 }
476
477 fn num_buckets(&self, width: u16, height: u16) -> u16 {
478 match self.column_width / u16::max(width, height) {
479 0 ..= 4 => 1,
480 5 ..= 16 => 2,
481 17 ..= 32 => 4,
482 n => (n /16 - 1).next_power_of_two(),
483 }.min((MAX_BIN_COUNT - self.buckets.len()) as u16)
484 }
485
486 fn deallocate_from_bucket(&mut self, id: AllocId) -> bool {
490 let bucket_index = (id.0 & BIN_MASK) as usize;
491 let generation = ((id.0 & GEN_MASK) >> 24 ) as u8;
492
493 let bucket = &mut self.buckets[bucket_index];
494
495 let expected_generation = bucket.generation.0;
496 assert_eq!(generation, expected_generation);
497
498 assert!(bucket.refcount > 0);
499 bucket.refcount -= 1;
500
501 let shelf = &self.shelves[bucket.shelf as usize];
502
503 let bucket_is_empty = bucket.refcount == 0;
504 if bucket_is_empty {
505 self.allocated_space -= (shelf.bucket_width - bucket.free_space) as i32 * shelf.height as i32;
506 bucket.free_space = shelf.bucket_width;
507 }
508
509 bucket_is_empty && bucket.shelf as usize == self.shelves.len() - 1
510 }
511
512 fn cleanup_shelves(&mut self) {
513 while self.shelves.len() > 0 {
514 {
515 let shelf = self.shelves.last().unwrap();
516 let mut bucket_index = shelf.first_bucket;
517 let mut last_bucket = shelf.first_bucket;
518
519 while bucket_index != BucketIndex::INVALID {
520 let bucket = &self.buckets[bucket_index.to_usize()];
521
522 if bucket.refcount != 0 {
523 return;
524 }
525
526 last_bucket = bucket_index;
527 bucket_index = bucket.next;
528 }
529
530 debug_assert!(last_bucket != BucketIndex::INVALID);
535 self.buckets[last_bucket.to_usize()].next = self.first_unallocated_bucket;
537 self.first_unallocated_bucket = shelf.first_bucket;
538
539 if shelf.y == 0 && self.current_column > 0 {
540 self.current_column -= 1;
541 let prev_shelf = &self.shelves[self.shelves.len() - 2];
542 self.available_height = self.height - (prev_shelf.y + prev_shelf.height);
543 } else {
544 self.available_height += shelf.height;
546 }
547 }
548
549 self.shelves.pop();
550 }
551 }
552
553 fn shelf_is_empty(&self, idx: usize) -> bool {
554 let shelf = &self.shelves[idx];
555 let mut bucket_index = shelf.first_bucket;
556
557 while bucket_index != BucketIndex::INVALID {
558 let bucket = &self.buckets[bucket_index.to_usize()];
559
560 if bucket.refcount != 0 {
561 return false;
562 }
563
564 bucket_index = bucket.next;
565 }
566
567 true
568 }
569
570
571 pub fn dump_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> {
573 use svg_fmt::*;
574
575 writeln!(
576 output,
577 "{}",
578 BeginSvg {
579 w: self.width as f32,
580 h: self.height as f32
581 }
582 )?;
583
584 self.dump_into_svg(None, output)?;
585
586 writeln!(output, "{}", EndSvg)
587 }
588
589
590 #[cfg(not(feature = "checks"))]
591 fn check(&self) {}
592
593 #[cfg(feature = "checks")]
594 fn check(&self) {
595 let mut h = 0;
596 for shelf in &self.shelves {
597 h += shelf.height;
598 }
599 h += self.available_height;
600
601 assert_eq!(h % self.height, 0);
603 assert!(h <= self.height * self.num_columns);
604 assert!(h >= self.height);
605
606 assert_eq!(self.is_empty(), self.allocated_space() == 0)
607 }
608
609 pub fn dump_into_svg(&self, rect: Option<&Rectangle>, output: &mut dyn std::io::Write) -> std::io::Result<()> {
614 use svg_fmt::*;
615
616 let (sx, sy, tx, ty) = if let Some(rect) = rect {
617 (
618 rect.size().width as f32 / self.width as f32,
619 rect.size().height as f32 / self.height as f32,
620 rect.min.x as f32,
621 rect.min.y as f32,
622 )
623 } else {
624 (1.0, 1.0, 0.0, 0.0)
625 };
626
627 writeln!(
628 output,
629 r#" {}"#,
630 rectangle(tx, ty, self.width as f32 * sx, self.height as f32 * sy)
631 .fill(rgb(40, 40, 40))
632 .stroke(Stroke::Color(black(), 1.0))
633 )?;
634
635
636 for shelf in &self.shelves {
637 let mut bucket_index = shelf.first_bucket;
638
639 let y = shelf.y as f32 * sy;
640 let h = shelf.height as f32 * sy;
641 while bucket_index != BucketIndex::INVALID {
642 let bucket = &self.buckets[bucket_index.to_usize()];
643
644 let x = bucket.x as f32 * sx;
645 let w = (shelf.bucket_width - bucket.free_space) as f32 * sx;
646
647 {
648 let (x, y) = if self.flip_xy { (y, x) } else { (x, y) };
649 let (w, h) = if self.flip_xy { (h, w) } else { (w, h) };
650
651 writeln!(
652 output,
653 r#" {}"#,
654 rectangle(x + tx, y + ty, w, h)
655 .fill(rgb(70, 70, 180))
656 .stroke(Stroke::Color(black(), 1.0))
657 )?;
658 }
659
660 if bucket.free_space > 0 {
661 let x_free = x + w;
662 let w_free = bucket.free_space as f32 * sx;
663
664 let (x_free, y) = if self.flip_xy { (y, x_free) } else { (x_free, y) };
665 let (w_free, h) = if self.flip_xy { (h, w_free) } else { (w_free, h) };
666
667 writeln!(
668 output,
669 r#" {}"#,
670 rectangle(x_free + tx, y + ty, w_free, h)
671 .fill(rgb(50, 50, 50))
672 .stroke(Stroke::Color(black(), 1.0))
673 )?;
674 }
675
676 bucket_index = bucket.next;
677 }
678 }
679
680 Ok(())
681 }
682}
683
684fn convert_coordinates(flip_xy: bool, x: u16, y: u16) -> (u16, u16) {
685 if flip_xy {
686 (y, x)
687 } else {
688 (x, y)
689 }
690}
691
692
693fn shelf_height(mut size: u16) -> u16 {
694 let alignment = match size {
695 0 ..= 31 => 8,
696 32 ..= 127 => 16,
697 128 ..= 511 => 32,
698 _ => 64,
699 };
700
701 let rem = size % alignment;
702 if rem > 0 {
703 size += alignment - rem;
704 }
705
706 size
707}
708
709fn adjust_size(alignment: i32, size: &mut i32) {
710 let rem = *size % alignment;
711 if rem > 0 {
712 *size += alignment - rem;
713 }
714}
715
716#[test]
717fn atlas_basic() {
718 let mut atlas = BucketedAtlasAllocator::new(size2(1000, 1000));
719
720 let full = atlas.allocate(size2(1000, 1000)).unwrap().id;
721 assert!(atlas.allocate(size2(1, 1)).is_none());
722
723 atlas.deallocate(full);
724 let a = atlas.allocate(size2(10, 10)).unwrap().id;
725 let b = atlas.allocate(size2(50, 30)).unwrap().id;
726 let c = atlas.allocate(size2(12, 45)).unwrap().id;
727 let d = atlas.allocate(size2(60, 45)).unwrap().id;
728 let e = atlas.allocate(size2(1, 1)).unwrap().id;
729 let f = atlas.allocate(size2(128, 128)).unwrap().id;
730 let g = atlas.allocate(size2(256, 256)).unwrap().id;
731
732 atlas.deallocate(b);
733 atlas.deallocate(f);
734 atlas.deallocate(c);
735 atlas.deallocate(e);
736 let h = atlas.allocate(size2(500, 200)).unwrap().id;
737 atlas.deallocate(a);
738 let i = atlas.allocate(size2(500, 200)).unwrap().id;
739 atlas.deallocate(g);
740 atlas.deallocate(h);
741 atlas.deallocate(d);
742 atlas.deallocate(i);
743
744 let full = atlas.allocate(size2(1000, 1000)).unwrap().id;
745 assert!(atlas.allocate(size2(1, 1)).is_none());
746 atlas.deallocate(full);
747}
748
749#[test]
750fn test_coalesce_shelves() {
751 let mut atlas = BucketedAtlasAllocator::new(size2(256, 256));
752
753 let mut ids = Vec::new();
755 for _ in 0..7 {
756 for _ in 0..8 {
757 ids.push(atlas.allocate(size2(32, 32)).unwrap().id)
758 }
759 }
760
761 for i in 0..8 {
763 atlas.deallocate(ids[i]);
764 }
765
766 for i in 16..32 {
768 atlas.deallocate(ids[i]);
769 }
770
771 assert!(atlas.allocate(size2(70, 70)).is_none());
774
775 let id = atlas.allocate(size2(64, 64)).unwrap().id;
778
779 for i in 8..16 {
781 atlas.deallocate(ids[i]);
782 }
783
784 atlas.deallocate(id);
785
786 for i in 32..56 {
787 atlas.deallocate(ids[i]);
788 }
789
790 assert!(atlas.is_empty());
793 assert_eq!(atlas.allocated_space(), 0);
794}
795
796#[test]
797fn grow_vertically() {
798 let mut atlas = BucketedAtlasAllocator::new(size2(256, 256));
799
800 let mut ids = Vec::new();
802 for _ in 0..7 {
803 for _ in 0..8 {
804 ids.push(atlas.allocate(size2(32, 32)).unwrap().id)
805 }
806 }
807
808 for i in 0..8 {
810 atlas.deallocate(ids[i]);
811 }
812
813 for i in 16..32 {
815 atlas.deallocate(ids[i]);
816 }
817
818 assert!(atlas.allocate(size2(70, 70)).is_none());
821
822 atlas.grow(size2(256, 256 + 70 - 32));
824
825 assert!(atlas.allocate(size2(70, 70)).is_some());
827}
828
829#[test]
830fn grow_horizontally() {
831 let mut atlas = BucketedAtlasAllocator::new(size2(256, 256));
832
833 let mut ids = Vec::new();
835 for _ in 0..7 {
836 for _ in 0..8 {
837 ids.push(atlas.allocate(size2(32, 32)).unwrap().id)
838 }
839 }
840
841 for i in 0..8 {
843 atlas.deallocate(ids[i]);
844 }
845
846 for i in 16..32 {
848 atlas.deallocate(ids[i]);
849 }
850
851 assert!(atlas.allocate(size2(512, 32)).is_none());
854
855 atlas.grow(size2(256 * 2, 256));
857
858 assert!(atlas.allocate(size2(512, 32)).is_some());
860}
861
862#[test]
863fn grow_to_fit_allocation() {
864 let mut atlas = BucketedAtlasAllocator::new(size2(32, 32));
865
866 atlas.allocate(size2(32, 32)).unwrap();
868
869 let big_allocation = size2(256, 256);
871
872 assert!(atlas.allocate(big_allocation).is_none());
873
874 atlas.grow(size2(256, 32 + 256));
876
877 assert!(atlas.allocate(size2(32, 32)).is_some());
879
880 assert!(atlas.allocate(big_allocation).is_some());
882}
883
884#[test]
885fn columns() {
886 let mut atlas = BucketedAtlasAllocator::with_options(size2(64, 64), &AllocatorOptions {
887 num_columns: 2,
888 ..DEFAULT_OPTIONS
889 });
890
891 let a = atlas.allocate(size2(24, 46)).unwrap();
892 let b = atlas.allocate(size2(24, 32)).unwrap();
893 let c = atlas.allocate(size2(24, 32)).unwrap();
894
895 fn in_range(val: i32, range: std::ops::Range<i32>) -> bool {
896 let ok = val >= range.start && val < range.end;
897
898 if !ok {
899 println!("{:?} not in {:?}", val, range);
900 }
901
902 ok
903 }
904
905 assert!(in_range(a.rectangle.min.x, 0..32));
906 assert!(in_range(a.rectangle.max.x, 0..32));
907 assert!(in_range(b.rectangle.min.x, 32..64));
908 assert!(in_range(b.rectangle.max.x, 32..64));
909 assert!(in_range(c.rectangle.min.x, 32..64));
910 assert!(in_range(c.rectangle.max.x, 32..64));
911
912 atlas.deallocate(b.id);
913 atlas.deallocate(c.id);
914 atlas.deallocate(a.id);
915
916 assert!(atlas.is_empty());
917 assert_eq!(atlas.allocated_space(), 0);
918
919 let a = atlas.allocate(size2(24, 46)).unwrap();
920 let b = atlas.allocate(size2(24, 32)).unwrap();
921 let c = atlas.allocate(size2(24, 32)).unwrap();
922 let d = atlas.allocate(size2(24, 8)).unwrap();
923
924 assert_eq!(a.rectangle.min.x, 0);
925 assert_eq!(b.rectangle.min.x, 32);
926 assert_eq!(c.rectangle.min.x, 32);
927 assert_eq!(d.rectangle.min.x, 0);
928}
929
930#[test]
931fn vertical() {
932 let mut atlas = BucketedAtlasAllocator::with_options(size2(128, 256), &AllocatorOptions {
933 num_columns: 2,
934 vertical_shelves: true,
935 ..DEFAULT_OPTIONS
936 });
937
938 assert_eq!(atlas.size(), size2(128, 256));
939
940 let a = atlas.allocate(size2(32, 16)).unwrap();
941 let b = atlas.allocate(size2(16, 32)).unwrap();
942
943 assert!(a.rectangle.size().width >= 32);
944 assert!(a.rectangle.size().height >= 16);
945
946 assert!(b.rectangle.size().width >= 16);
947 assert!(b.rectangle.size().height >= 32);
948
949 let c = atlas.allocate(size2(128, 128)).unwrap();
950
951 atlas.deallocate(a.id);
952 atlas.deallocate(b.id);
953 atlas.deallocate(c.id);
954
955 assert!(atlas.is_empty());
956 assert_eq!(atlas.allocated_space(), 0);
957}
958
959#[test]
960fn clear() {
961 let mut atlas = BucketedAtlasAllocator::new(size2(2048, 2048));
962
963 for _ in 0..500 {
965 atlas.clear();
966 assert_eq!(atlas.allocated_space(), 0);
967
968 atlas.allocate(size2(8, 2)).unwrap();
969 atlas.allocate(size2(2, 8)).unwrap();
970 atlas.allocate(size2(16, 512)).unwrap();
971 atlas.allocate(size2(34, 34)).unwrap();
972 atlas.allocate(size2(34, 34)).unwrap();
973 atlas.allocate(size2(34, 34)).unwrap();
974 atlas.allocate(size2(34, 34)).unwrap();
975 atlas.allocate(size2(2, 8)).unwrap();
976 atlas.allocate(size2(2, 8)).unwrap();
977 atlas.allocate(size2(8, 2)).unwrap();
978 atlas.allocate(size2(2, 8)).unwrap();
979 atlas.allocate(size2(8, 2)).unwrap();
980 atlas.allocate(size2(8, 8)).unwrap();
981 atlas.allocate(size2(8, 8)).unwrap();
982 atlas.allocate(size2(8, 8)).unwrap();
983 atlas.allocate(size2(8, 8)).unwrap();
984 atlas.allocate(size2(82, 80)).unwrap();
985 atlas.allocate(size2(56, 56)).unwrap();
986 atlas.allocate(size2(64, 66)).unwrap();
987 atlas.allocate(size2(32, 32)).unwrap();
988 atlas.allocate(size2(32, 32)).unwrap();
989 atlas.allocate(size2(32, 32)).unwrap();
990 atlas.allocate(size2(32, 32)).unwrap();
991 atlas.allocate(size2(32, 32)).unwrap();
992 atlas.allocate(size2(32, 32)).unwrap();
993 atlas.allocate(size2(32, 32)).unwrap();
994 atlas.allocate(size2(32, 32)).unwrap();
995 atlas.allocate(size2(32, 32)).unwrap();
996 atlas.allocate(size2(40, 40)).unwrap();
997 atlas.allocate(size2(32, 32)).unwrap();
998 atlas.allocate(size2(256, 52)).unwrap();
999 atlas.allocate(size2(32, 32)).unwrap();
1000 atlas.allocate(size2(256, 52)).unwrap();
1001 atlas.allocate(size2(256, 52)).unwrap();
1002 atlas.allocate(size2(256, 52)).unwrap();
1003 atlas.allocate(size2(256, 52)).unwrap();
1004 atlas.allocate(size2(256, 52)).unwrap();
1005 atlas.allocate(size2(256, 52)).unwrap();
1006 atlas.allocate(size2(155, 52)).unwrap();
1007 atlas.allocate(size2(256, 52)).unwrap();
1008 atlas.allocate(size2(32, 32)).unwrap();
1009 atlas.allocate(size2(32, 32)).unwrap();
1010 atlas.allocate(size2(32, 32)).unwrap();
1011 atlas.allocate(size2(24, 24)).unwrap();
1012 atlas.allocate(size2(64, 64)).unwrap();
1013 atlas.allocate(size2(32, 32)).unwrap();
1014 atlas.allocate(size2(84, 84)).unwrap();
1015 atlas.allocate(size2(32, 32)).unwrap();
1016 atlas.allocate(size2(8, 2)).unwrap();
1017 atlas.allocate(size2(34, 34)).unwrap();
1018 atlas.allocate(size2(34, 34)).unwrap();
1019 atlas.allocate(size2(192, 192)).unwrap();
1020 atlas.allocate(size2(192, 192)).unwrap();
1021 atlas.allocate(size2(52, 52)).unwrap();
1022 atlas.allocate(size2(144, 144)).unwrap();
1023 atlas.allocate(size2(192, 192)).unwrap();
1024 atlas.allocate(size2(32, 32)).unwrap();
1025 atlas.allocate(size2(144, 144)).unwrap();
1026 atlas.allocate(size2(24, 24)).unwrap();
1027 atlas.allocate(size2(192, 192)).unwrap();
1028 atlas.allocate(size2(192, 192)).unwrap();
1029 atlas.allocate(size2(432, 243)).unwrap();
1030 atlas.allocate(size2(32, 32)).unwrap();
1031 atlas.allocate(size2(8, 2)).unwrap();
1032 atlas.allocate(size2(2, 8)).unwrap();
1033 atlas.allocate(size2(9, 9)).unwrap();
1034 atlas.allocate(size2(14, 14)).unwrap();
1035 atlas.allocate(size2(14, 14)).unwrap();
1036 atlas.allocate(size2(14, 14)).unwrap();
1037 atlas.allocate(size2(14, 14)).unwrap();
1038 atlas.allocate(size2(8, 8)).unwrap();
1039 atlas.allocate(size2(27, 27)).unwrap();
1040 atlas.allocate(size2(27, 27)).unwrap();
1041 atlas.allocate(size2(27, 27)).unwrap();
1042 atlas.allocate(size2(27, 27)).unwrap();
1043 atlas.allocate(size2(11, 12)).unwrap();
1044 atlas.allocate(size2(29, 28)).unwrap();
1045 atlas.allocate(size2(32, 32)).unwrap();
1046 }
1047}
1048
1049
1050#[test]
1051fn fuzz_01() {
1052 let mut atlas = BucketedAtlasAllocator::new(size2(1000, 1000));
1053
1054 assert!(atlas.allocate(size2(65280, 1)).is_none());
1055 assert!(atlas.allocate(size2(1, 65280)).is_none());
1056}
1057
1058#[test]
1059fn fuzz_02() {
1060 let mut atlas = BucketedAtlasAllocator::new(size2(1000, 1000));
1061
1062 assert!(atlas.allocate(size2(255, 65599)).is_none());
1063}
1064
1065#[test]
1066fn fuzz_03() {
1067 let mut atlas = BucketedAtlasAllocator::new(size2(1000, 1000));
1068
1069 let sizes = &[
1070 size2(999, 128),
1071 size2(168492810, 10),
1072 size2(45, 96),
1073 size2(-16711926, 0),
1074 ];
1075
1076 let mut allocations = Vec::new();
1077 let mut allocated_space = 0;
1078
1079 for size in sizes {
1080 if let Some(alloc) = atlas.allocate(*size) {
1081 allocations.push(alloc);
1082 allocated_space += alloc.rectangle.area();
1083 assert_eq!(allocated_space, atlas.allocated_space());
1084 }
1085 }
1086
1087 for alloc in &allocations {
1088 atlas.deallocate(alloc.id);
1089
1090 allocated_space -= alloc.rectangle.area();
1091 assert_eq!(allocated_space, atlas.allocated_space());
1092 }
1093
1094 assert_eq!(atlas.allocated_space(), 0);
1095}
1096
1097#[test]
1098fn fuzz_04() {
1099 let mut atlas = BucketedAtlasAllocator::new(size2(1000, 1000));
1100
1101 assert!(atlas.allocate(size2(2560, 2147483647)).is_none());
1102}
1103
1104#[test]
1105fn fuzz_05() {
1106 let mut atlas = BucketedAtlasAllocator::new(size2(2048, 2048));
1107
1108 assert!(atlas.allocate(size2(0, -1978597547)).is_none());
1109}