1use smithay::utils::{Physical, Rectangle};
33
34pub const BLOCK: i32 = 16;
38
39pub const REFINE_FINER: u32 = 6;
45
46const REFINE_FRAMES: usize = 64;
51const REFINE_START_FRAMES: usize = 512;
52
53const REFINE_RISE: i32 = 2;
59
60const REFINE_OVER: f64 = 1.5;
65
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
72pub struct Refiner {
73 pub finer: u32,
74 pub reach: u32,
75 pub runs: usize,
76}
77
78impl Refiner {
79 pub const fn finer(finer: u32) -> Self {
81 Self {
82 finer,
83 reach: u32::MAX,
84 runs: usize::MAX,
85 }
86 }
87}
88
89#[derive(Clone, Debug, PartialEq)]
93pub struct Refinement {
94 pub plan: BlockPlan,
95 pub target: i32,
96 pub from: i32,
97 pub reach: u32,
98}
99
100impl Refinement {
101 pub fn offset(&self) -> i32 {
103 (self.target - self.from).clamp(-(self.reach.min(i32::MAX as u32) as i32), 0)
104 }
105}
106
107const REFINE_PAUSE: u32 = 64;
113
114const REFINE_RATE_HELD: f64 = 1.2;
120
121#[derive(Clone, Debug, Default)]
124pub struct RefinePace {
125 count: usize,
126 base: Option<i32>,
127 base_bytes: f64,
129 paused: u32,
130}
131
132impl RefinePace {
133 pub fn most(&mut self, blocks: usize, constant_rate: bool) -> usize {
135 let most = blocks.div_ceil(REFINE_FRAMES);
136 if !constant_rate {
137 return most;
138 }
139 if self.paused > 0 {
140 self.paused -= 1;
141 return 0;
142 }
143 if self.count == 0 {
144 self.count = blocks.div_ceil(REFINE_START_FRAMES);
145 }
146 self.count.min(most)
147 }
148
149 pub fn coded(&mut self, refined: bool, coded: i32, bytes: usize, budget: f64, blocks: usize) {
152 if !refined {
153 self.base = Some(coded);
154 self.base_bytes += (bytes as f64 - self.base_bytes) / 8.0;
155 if self.base_bytes > budget * REFINE_RATE_HELD {
156 self.paused = REFINE_PAUSE;
157 }
158 return;
159 }
160 let base = *self.base.get_or_insert(coded);
161 let budget = budget.max(self.base_bytes);
162 let most = blocks.div_ceil(REFINE_FRAMES);
163 let count = self.count;
164 let costly = bytes as f64 > budget * REFINE_OVER || coded > base + REFINE_RISE;
165 if costly && count <= 1 {
166 self.paused = REFINE_PAUSE;
167 }
168 self.count = if costly {
169 (count / 2).max(1)
170 } else if coded <= base {
171 (count + count / 4 + 1).min(most)
172 } else {
173 count
174 };
175 }
176}
177
178pub const REFRESH_FINER: u32 = 3;
185
186pub const REFRESH_KEY_FINER: u32 = 3;
192
193const REFRESH_FRAMES: usize = 256;
199
200const REFRESH_PAUSE: u32 = 60;
207
208#[derive(Clone, Debug, Default)]
210pub struct RefreshPace {
211 count: usize,
212 paused: u32,
213}
214
215impl RefreshPace {
216 pub fn most(&mut self, blocks: usize) -> usize {
218 if self.paused > 0 {
219 self.paused -= 1;
220 return 0;
221 }
222 if self.count == 0 {
223 self.count = blocks.div_ceil(REFRESH_FRAMES).max(1);
224 }
225 self.count
226 }
227
228 pub fn coded(&mut self, bytes: usize, budget: f64, blocks: usize) {
230 let count = self.count.max(1);
231 if bytes as f64 > budget && count == 1 {
232 self.paused = REFRESH_PAUSE;
233 }
234 self.count = if bytes as f64 > budget {
235 (count / 2).max(1)
236 } else if (bytes as f64) < budget / 2.0 {
237 count.saturating_mul(2).min(blocks.max(1))
238 } else {
239 count
240 };
241 }
242}
243
244#[derive(Clone, Debug, PartialEq)]
246pub struct BlockPlan {
247 pub cols: usize,
248 pub rows: usize,
249 refine: Vec<u64>,
250 pub count: usize,
252}
253
254impl BlockPlan {
255 pub fn none_like(&self) -> Self {
257 Self {
258 cols: self.cols,
259 rows: self.rows,
260 refine: vec![0; self.refine.len()],
261 count: 0,
262 }
263 }
264
265 pub fn refines(&self, col: usize, row: usize) -> bool {
267 let i = row * self.cols + col;
268 col < self.cols && row < self.rows && self.refine[i / 64] >> (i % 64) & 1 != 0
269 }
270
271 pub fn refines_area(&self, x: i32, y: i32, width: i32, height: i32) -> bool {
274 let c0 = (x.max(0) / BLOCK) as usize;
275 let r0 = (y.max(0) / BLOCK) as usize;
276 let c1 = (((x + width).max(0) + BLOCK - 1) / BLOCK) as usize;
277 let r1 = (((y + height).max(0) + BLOCK - 1) / BLOCK) as usize;
278 (r0..r1.min(self.rows)).any(|r| (c0..c1.min(self.cols)).any(|c| self.refines(c, r)))
279 }
280}
281
282#[derive(Clone, Debug, Default)]
284pub struct BlockLedger {
285 width: i32,
286 height: i32,
287 cols: usize,
288 rows: usize,
289 still: Vec<u16>,
291 clean: Vec<bool>,
293 refined: Vec<bool>,
296 coarse: Vec<bool>,
300 coarse_key: Vec<bool>,
301 coarse_count: usize,
303 owed: Vec<bool>,
308 owed_count: usize,
309 fresh: Vec<bool>,
313 fresh_count: usize,
314 next_refresh: usize,
316 next: usize,
318 unreported: bool,
321}
322
323impl BlockLedger {
324 pub fn new(width: i32, height: i32) -> Self {
326 let cols = (width.max(0) + BLOCK - 1) as usize / BLOCK as usize;
327 let rows = (height.max(0) + BLOCK - 1) as usize / BLOCK as usize;
328 Self {
329 width,
330 height,
331 cols,
332 rows,
333 still: vec![0; cols * rows],
334 clean: vec![false; cols * rows],
335 refined: vec![false; cols * rows],
336 coarse: vec![false; cols * rows],
337 coarse_key: vec![false; cols * rows],
338 coarse_count: 0,
339 owed: vec![false; cols * rows],
340 owed_count: 0,
341 fresh: vec![false; cols * rows],
342 fresh_count: 0,
343 next_refresh: 0,
344 next: 0,
345 unreported: true,
346 }
347 }
348
349 pub fn fit(&mut self, width: i32, height: i32) {
352 if (width, height) != (self.width, self.height) {
353 *self = Self::new(width, height);
354 }
355 }
356
357 pub fn frame<'a>(&mut self, rects: impl IntoIterator<Item = &'a Rectangle<i32, Physical>>) {
361 for s in self.still.iter_mut() {
362 *s = s.saturating_add(1);
363 }
364 for r in rects {
365 let c0 = (r.loc.x.max(0) / BLOCK) as usize;
366 let r0 = (r.loc.y.max(0) / BLOCK) as usize;
367 let c1 = ((r.loc.x + r.size.w).clamp(0, self.width) + BLOCK - 1) / BLOCK;
368 let r1 = ((r.loc.y + r.size.h).clamp(0, self.height) + BLOCK - 1) / BLOCK;
369 for row in r0..(r1 as usize).min(self.rows) {
370 for col in c0..(c1 as usize).min(self.cols) {
371 let i = row * self.cols + col;
372 self.still[i] = 0;
373 self.clean[i] = false;
374 self.refined[i] = false;
375 }
376 }
377 }
378 }
379
380 pub fn frame_reported(
389 &mut self,
390 rects: &[Rectangle<i32, Physical>],
391 dirty: bool,
392 observed: bool,
393 ) {
394 if observed {
395 self.unreported = rects.is_empty();
396 }
397 if rects.is_empty() && dirty && self.unreported {
398 self.frame_all();
399 } else {
400 self.frame(rects);
401 }
402 }
403
404 pub fn frame_all(&mut self) {
406 self.still.fill(0);
407 self.clean.fill(false);
408 self.refined.fill(false);
409 }
410
411 pub fn coarsened(&mut self) {
414 self.clean.fill(false);
415 self.refined.fill(false);
416 }
417
418 pub fn cleaned(&mut self) {
421 for i in 0..self.still.len() {
422 if self.still[i] == 0 {
423 continue;
424 }
425 self.clean[i] = true;
426 if self.coarse[i] {
427 self.coarse[i] = false;
428 self.coarse_count -= 1;
429 }
430 if self.owed[i] {
431 self.owed[i] = false;
432 self.owed_count -= 1;
433 }
434 self.unfresh(i);
435 }
436 }
437
438 fn unfresh(&mut self, i: usize) {
440 if self.fresh[i] {
441 self.fresh[i] = false;
442 self.fresh_count -= 1;
443 }
444 }
445
446 pub fn spent(&mut self) -> bool {
451 let before = self.owed_count;
452 for i in 0..self.still.len() {
453 if self.still[i] == 0 {
454 continue;
455 }
456 self.clean[i] = false;
457 self.refined[i] = false;
458 if !self.coarse[i] {
459 self.coarse[i] = true;
460 self.coarse_count += 1;
461 }
462 if !self.owed[i] {
463 self.owed[i] = true;
464 self.owed_count += 1;
465 }
466 self.unfresh(i);
467 }
468 self.owed_count > before
469 }
470
471 pub fn settled(&self) -> bool {
473 self.clean
474 .iter()
475 .zip(&self.still)
476 .all(|(&c, &s)| c || s == 0)
477 }
478
479 pub fn plan(&mut self, trigger: u32, max: usize, runs: usize) -> Option<BlockPlan> {
483 let n = self.cols * self.rows;
484 if n == 0 || max == 0 {
485 return None;
486 }
487 let trigger = trigger.min(u32::from(u16::MAX)) as u16;
488 let mut refine = vec![0u64; n.div_ceil(64)];
489 let (mut count, mut started) = (0, 0);
490 let mut last: Option<usize> = None;
491 let start = self.next % n;
492 for k in 0..n {
493 let i = (start + k) % n;
494 if self.still[i] >= trigger && !self.clean[i] {
495 let continues = last.is_some_and(|l| l + 1 == i && !i.is_multiple_of(self.cols));
496 if !continues {
497 if started == runs {
498 self.next = i;
499 break;
500 }
501 started += 1;
502 }
503 refine[i / 64] |= 1 << (i % 64);
504 last = Some(i);
505 count += 1;
506 if count == max {
507 self.next = i + 1;
508 break;
509 }
510 }
511 }
512 (count > 0).then_some(BlockPlan {
513 cols: self.cols,
514 rows: self.rows,
515 refine,
516 count,
517 })
518 }
519
520 pub fn refinement_coded(&mut self, refinement: &Refinement, coded: i32) {
525 let at_reach = refinement.offset() <= -(refinement.reach.min(i32::MAX as u32) as i32);
526 if coded + refinement.offset() <= refinement.target || at_reach {
527 self.refined(&refinement.plan);
528 }
529 }
530
531 pub fn refined(&mut self, plan: &BlockPlan) {
533 for row in 0..self.rows.min(plan.rows) {
534 for col in 0..self.cols.min(plan.cols) {
535 if plan.refines(col, row) {
536 self.clean[row * self.cols + col] = true;
537 self.refined[row * self.cols + col] = true;
538 }
539 }
540 }
541 }
542
543 pub fn unrefined_share(&self) -> f32 {
547 if self.refined.is_empty() {
548 return 1.0;
549 }
550 let unrefined = self.refined.iter().filter(|&&r| !r).count();
551 unrefined as f32 / self.refined.len() as f32
552 }
553
554 pub fn unchanged(&self) -> impl Iterator<Item = bool> + '_ {
557 self.still.iter().map(|&s| s > 0)
558 }
559
560 pub fn constant(&self) -> impl Iterator<Item = bool> + '_ {
566 self.still
567 .iter()
568 .zip(&self.clean)
569 .map(|(&still, &clean)| still > 0 && clean)
570 }
571
572 pub fn blocks(&self) -> usize {
574 self.cols * self.rows
575 }
576
577 pub fn coded(&mut self, capped: bool, whole: bool) {
583 if !capped && self.coarse_count == 0 && self.fresh_count == 0 {
584 return;
585 }
586 for i in 0..self.coarse.len() {
587 if whole || self.still[i] == 0 {
588 match (self.coarse[i], capped) {
589 (false, true) => self.coarse_count += 1,
590 (true, false) => self.coarse_count -= 1,
591 _ => {}
592 }
593 self.coarse[i] = capped;
594 self.coarse_key[i] = capped && whole;
595 if self.owed[i] {
596 self.owed[i] = false;
597 self.owed_count -= 1;
598 }
599 self.unfresh(i);
600 if capped {
601 self.clean[i] = false;
602 self.refined[i] = false;
603 }
604 }
605 }
606 }
607
608 pub fn refresh(&mut self, max: usize) -> Option<(BlockPlan, BlockPlan)> {
612 self.refresh_keyed(max).map(|(now, _, wait)| (now, wait))
613 }
614
615 pub fn refresh_keyed(&mut self, max: usize) -> Option<(BlockPlan, BlockPlan, BlockPlan)> {
619 let n = self.cols * self.rows;
620 if n == 0 || self.coarse_count == 0 {
621 return None;
622 }
623 let mut now = vec![0u64; n.div_ceil(64)];
624 let mut key = vec![0u64; n.div_ceil(64)];
625 let mut wait = vec![0u64; n.div_ceil(64)];
626 let (mut count, mut keyed, mut waiting) = (0, 0, 0);
627 let start = self.next_refresh % n;
628 for k in 0..n {
629 let i = (start + k) % n;
630 if !self.coarse[i] || self.still[i] == 0 {
631 continue;
632 }
633 if count < max.max(1) && !self.owed[i] {
634 now[i / 64] |= 1 << (i % 64);
635 count += 1;
636 if self.coarse_key[i] {
637 key[i / 64] |= 1 << (i % 64);
638 keyed += 1;
639 }
640 self.next_refresh = i + 1;
641 } else {
642 wait[i / 64] |= 1 << (i % 64);
643 waiting += 1;
644 }
645 }
646 let plan = |refine, count| BlockPlan {
647 cols: self.cols,
648 rows: self.rows,
649 refine,
650 count,
651 };
652 (count > 0).then(|| (plan(now, count), plan(key, keyed), plan(wait, waiting)))
653 }
654
655 pub fn refresh_owed(&mut self, max: usize) -> Option<(BlockPlan, BlockPlan)> {
660 let n = self.cols * self.rows;
661 if n == 0 || self.owed_count == 0 {
662 return None;
663 }
664 let mut now = vec![0u64; n.div_ceil(64)];
665 let mut wait = vec![0u64; n.div_ceil(64)];
666 let (mut count, mut waiting) = (0, 0);
667 let start = self.next_refresh % n;
668 for k in 0..n {
669 let i = (start + k) % n;
670 if !self.coarse[i] || self.still[i] == 0 {
671 continue;
672 }
673 if self.owed[i] && count < max.max(1) {
674 now[i / 64] |= 1 << (i % 64);
675 count += 1;
676 self.next_refresh = i + 1;
677 } else {
678 wait[i / 64] |= 1 << (i % 64);
679 waiting += 1;
680 }
681 }
682 let plan = |refine, count| BlockPlan {
683 cols: self.cols,
684 rows: self.rows,
685 refine,
686 count,
687 };
688 (count > 0).then(|| (plan(now, count), plan(wait, waiting)))
689 }
690
691 pub fn refreshed(&mut self, plan: &BlockPlan) {
695 for row in 0..self.rows.min(plan.rows) {
696 for col in 0..self.cols.min(plan.cols) {
697 let i = row * self.cols + col;
698 if !plan.refines(col, row) {
699 continue;
700 }
701 if self.coarse[i] {
702 self.coarse[i] = false;
703 self.coarse_count -= 1;
704 }
705 if self.owed[i] {
706 self.owed[i] = false;
707 self.owed_count -= 1;
708 self.clean[i] = true;
709 self.refined[i] = true;
710 } else if !self.fresh[i] {
711 self.fresh[i] = true;
712 self.fresh_count += 1;
713 }
714 }
715 }
716 }
717
718 pub fn owed(&self) -> bool {
720 self.owed_count > 0
721 }
722
723 pub fn fresh(&self) -> bool {
725 self.fresh_count > 0
726 }
727
728 pub fn coarse(&self) -> bool {
730 self.coarse_count > 0
731 }
732}
733
734#[cfg(test)]
735mod tests {
736 use super::*;
737
738 #[test]
742 fn a_dirty_frame_without_a_change_keeps_what_was_refined() {
743 let mut ledger = BlockLedger::new(64, 64);
744 let rect = [Rectangle::new((0, 0).into(), (16, 16).into())];
745 ledger.frame_reported(&rect, true, true);
746 let plan = ledger.plan(0, usize::MAX, usize::MAX).unwrap();
747 ledger.refined(&plan);
748 assert_eq!(ledger.unrefined_share(), 0.0);
749 ledger.frame_reported(&[], true, false);
750 assert_eq!(ledger.unrefined_share(), 0.0);
751 ledger.frame_reported(&[], true, true);
752 assert_eq!(ledger.unrefined_share(), 1.0);
753 let plan = ledger.plan(0, usize::MAX, usize::MAX).unwrap();
754 ledger.refined(&plan);
755 ledger.frame_reported(&[], true, false);
756 assert_eq!(ledger.unrefined_share(), 1.0);
757 }
758
759 #[test]
764 fn a_still_clean_block_is_constant_until_it_changes() {
765 let mut ledger = BlockLedger::new(64, 32);
766 ledger.frame([]);
767 assert!(ledger.constant().all(|c| !c), "nothing is clean yet");
768 ledger.cleaned();
769 ledger.frame([]);
770 assert!(ledger.constant().all(|c| c));
771 ledger.frame([&Rectangle::new((0, 0).into(), (16, 16).into())]);
772 let constant: Vec<bool> = ledger.constant().collect();
773 assert_eq!(
774 constant,
775 vec![false, true, true, true, true, true, true, true]
776 );
777 ledger.frame([]);
778 assert!(!ledger.constant().next().unwrap(), "still but unclean");
779 }
780
781 #[test]
782 fn a_refresh_cleans_without_refining() {
783 let mut ledger = BlockLedger::new(64, 64);
784 ledger.frame([]);
785 ledger.cleaned();
786 assert!(ledger.settled());
787 assert_eq!(ledger.unrefined_share(), 1.0);
788 }
789
790 fn rect(x: i32, y: i32, w: i32, h: i32) -> Rectangle<i32, Physical> {
791 Rectangle::new((x, y).into(), (w, h).into())
792 }
793
794 #[test]
795 fn blocks_that_held_still_are_refined_while_others_keep_changing() {
796 let mut ledger = BlockLedger::new(64, 32);
797 let video = rect(32, 0, 32, 32);
798 for _ in 0..3 {
799 ledger.frame([&video]);
800 }
801 let plan = ledger.plan(3, usize::MAX, usize::MAX).expect("a plan");
802 assert_eq!(plan.count, 4, "the two still columns of two rows");
803 assert!(plan.refines(0, 0) && plan.refines(1, 1) && !plan.refines(2, 0));
804 ledger.refined(&plan);
805 ledger.frame([&video]);
806 assert_eq!(
807 ledger.plan(3, usize::MAX, usize::MAX),
808 None,
809 "nothing left to refine"
810 );
811 assert!(ledger.settled());
812 }
813
814 #[test]
815 fn a_large_set_is_refined_in_turn() {
816 let mut ledger = BlockLedger::new(64, 64);
817 ledger.frame([]);
818 let first = ledger.plan(1, 6, usize::MAX).unwrap();
819 ledger.refined(&first);
820 let second = ledger.plan(1, 6, usize::MAX).unwrap();
821 assert_eq!((first.count, second.count), (6, 6));
822 assert!(
823 first.refines(0, 0) && second.refines(2, 1),
824 "the second takes up where the first stopped"
825 );
826 ledger.refined(&second);
827 let rest = ledger.plan(1, 16, usize::MAX).unwrap();
828 assert_eq!(rest.count, 4);
829 }
830
831 #[test]
832 fn a_change_or_a_coarse_key_frame_makes_blocks_unclean_again() {
833 let mut ledger = BlockLedger::new(32, 32);
834 ledger.frame([]);
835 ledger.cleaned();
836 assert!(ledger.settled());
837 ledger.frame([&rect(0, 0, 1, 1)]);
838 assert!(ledger.settled(), "a block that just changed is not due");
839 ledger.frame([]);
840 assert!(!ledger.settled());
841 assert_eq!(ledger.plan(1, 8, usize::MAX).unwrap().count, 1);
842 ledger.cleaned();
843 ledger.coarsened();
844 assert_eq!(ledger.plan(1, 8, usize::MAX).unwrap().count, 4);
845 }
846
847 #[test]
848 fn a_coarser_map_unit_refines_where_it_holds_a_refined_block() {
849 let mut ledger = BlockLedger::new(64, 64);
850 ledger.frame([&rect(0, 0, 64, 48)]);
851 let plan = ledger.plan(1, usize::MAX, usize::MAX).unwrap();
852 assert!(!plan.refines_area(0, 0, 32, 32));
853 assert!(
854 plan.refines_area(32, 32, 32, 32),
855 "the bottom row of blocks"
856 );
857 }
858
859 #[test]
860 fn a_rate_that_cannot_afford_a_block_a_frame_pauses_refinement() {
861 let mut pace = RefinePace::default();
862 assert_eq!(pace.most(512, true), 1);
863 pace.coded(true, 30, 3000, 1000.0, 512);
864 assert_eq!(
865 pace.most(512, true),
866 0,
867 "a block a frame over budget pauses"
868 );
869 for _ in 1..REFINE_PAUSE {
870 assert_eq!(pace.most(512, true), 0);
871 }
872 assert_eq!(pace.most(512, true), 1, "and resumes after the pause");
873 assert_eq!(
874 pace.most(512, false),
875 8,
876 "a constant quality refines its share"
877 );
878 }
879
880 #[test]
881 fn a_plan_keeps_to_the_runs_an_encoder_takes() {
882 let mut ledger = BlockLedger::new(64, 64);
883 ledger.frame([&rect(16, 0, 16, 64)]);
884 ledger.frame([&rect(16, 0, 16, 64)]);
885 let plan = ledger.plan(1, usize::MAX, 3).unwrap();
886 assert_eq!(
887 plan.count, 4,
888 "column 0 and columns 2 and 3 of row 0, then column 0 of row 1"
889 );
890 assert!(
891 plan.refines(0, 0) && plan.refines(2, 0) && plan.refines(3, 0) && plan.refines(0, 1)
892 );
893 assert!(!plan.refines(2, 1));
894 }
895
896 #[test]
897 fn a_block_refined_at_the_encoders_reach_counts_clean() {
898 let mut ledger = BlockLedger::new(16, 16);
899 ledger.frame([]);
900 let plan = ledger.plan(1, 8, usize::MAX).unwrap();
901 let reached = Refinement {
902 plan: plan.clone(),
903 target: 10,
904 from: 40,
905 reach: 10,
906 };
907 assert_eq!(reached.offset(), -10);
908 ledger.refinement_coded(&reached, 40);
909 assert!(ledger.settled(), "it could go no finer in one frame");
910 ledger.coarsened();
911 let short = Refinement {
912 plan,
913 target: 10,
914 from: 40,
915 reach: u32::MAX,
916 };
917 ledger.refinement_coded(&short, 45);
918 assert!(!ledger.settled(), "the frame came out coarser than planned");
919 }
920
921 #[test]
925 fn coarse_blocks_are_refreshed_once_in_turn() {
926 let mut ledger = BlockLedger::new(64, 16);
927 ledger.frame([]);
928 ledger.cleaned();
929 ledger.frame([&Rectangle::new((0, 0).into(), (48, 16).into())]);
930 ledger.coded(true, false);
931 assert!(ledger.coarse());
932 assert!(
933 ledger.constant().take(3).all(|c| !c),
934 "a capped block is unclean"
935 );
936 assert!(ledger.refresh(8).is_none(), "nothing coarse held still yet");
937 ledger.frame([]);
938 let (now, wait) = ledger.refresh(2).unwrap();
939 assert_eq!((now.count, wait.count), (2, 1));
940 assert!(now.refines(0, 0) && now.refines(1, 0) && wait.refines(2, 0));
941 assert!(
942 !now.refines(3, 0) && !wait.refines(3, 0),
943 "block 3 was never coarse"
944 );
945 ledger.refreshed(&now);
946 ledger.frame([]);
947 let (now, wait) = ledger.refresh(2).unwrap();
948 assert_eq!((now.count, wait.count), (1, 0));
949 assert!(now.refines(2, 0));
950 ledger.refreshed(&now);
951 assert!(!ledger.coarse() && ledger.refresh(2).is_none());
952 ledger.frame([&Rectangle::new((0, 0).into(), (16, 16).into())]);
953 ledger.coded(true, false);
954 ledger.frame([&Rectangle::new((0, 0).into(), (16, 16).into())]);
955 ledger.coded(false, false);
956 assert!(!ledger.coarse(), "coded again at the session's quality");
957 ledger.frame([]);
958 ledger.coded(true, true);
959 ledger.frame([]);
960 assert_eq!(
961 ledger.refresh(usize::MAX).unwrap().0.count,
962 4,
963 "a capped key frame"
964 );
965 }
966
967 #[test]
971 fn a_spent_cleanup_is_owed_to_the_blocks_that_held_still() {
972 let mut ledger = BlockLedger::new(64, 16);
973 ledger.frame([&Rectangle::new((0, 0).into(), (16, 16).into())]);
974 ledger.coded(true, false);
975 ledger.frame([&Rectangle::new((48, 0).into(), (16, 16).into())]);
976 ledger.coded(true, false);
977 ledger.spent();
978 assert!(ledger.owed() && ledger.coarse());
979 assert!(!ledger.settled(), "an owed block is unclean");
980 ledger.frame([]);
981 let (now, wait) = ledger.refresh_owed(1).unwrap();
982 assert_eq!((now.count, wait.count), (1, 3));
983 assert!(
984 !now.refines(3, 0),
985 "block 3 changed on the cleanup frame: nothing owed"
986 );
987 let (plain, _) = ledger.refresh(usize::MAX).unwrap();
988 assert!(
989 plain.refines(3, 0) && (0..3).all(|c| !plain.refines(c, 0)),
990 "an owed block waits for the owed refresh"
991 );
992 ledger.refreshed(&now);
993 ledger.frame([]);
994 let (now, wait) = ledger.refresh_owed(usize::MAX).unwrap();
995 assert_eq!((now.count, wait.count), (2, 1));
996 ledger.refreshed(&now);
997 assert!(!ledger.owed() && ledger.coarse());
998 assert!(
999 ledger.clean[..3].iter().all(|&c| c),
1000 "the owed blocks are clean"
1001 );
1002 ledger.refreshed(&plain);
1003 assert!(!ledger.coarse() && !ledger.clean[3]);
1004 }
1005
1006 #[test]
1009 fn the_coarse_count_follows_the_coarse_blocks() {
1010 let tally = |l: &BlockLedger| {
1011 assert_eq!(l.coarse_count, l.coarse.iter().filter(|&&c| c).count());
1012 assert_eq!(l.owed_count, l.owed.iter().filter(|&&c| c).count());
1013 assert!(l.owed.iter().zip(&l.coarse).all(|(&o, &c)| !o || c));
1014 assert_eq!(l.fresh_count, l.fresh.iter().filter(|&&c| c).count());
1015 };
1016 let mut ledger = BlockLedger::new(64, 32);
1017 ledger.frame([&Rectangle::new((0, 0).into(), (48, 16).into())]);
1018 ledger.coded(true, false);
1019 tally(&ledger);
1020 ledger.frame([&Rectangle::new((16, 0).into(), (32, 32).into())]);
1021 ledger.coded(true, false);
1022 tally(&ledger);
1023 ledger.frame([]);
1024 ledger.cleaned();
1025 tally(&ledger);
1026 ledger.frame([]);
1027 ledger.coded(true, true);
1028 tally(&ledger);
1029 ledger.frame([]);
1030 let (now, _) = ledger.refresh(3).unwrap();
1031 ledger.refreshed(&now);
1032 ledger.refreshed(&now);
1033 tally(&ledger);
1034 ledger.frame([&Rectangle::new((0, 0).into(), (16, 16).into())]);
1035 ledger.spent();
1036 tally(&ledger);
1037 ledger.frame([]);
1038 let (now, _) = ledger.refresh_owed(2).unwrap();
1039 ledger.refreshed(&now);
1040 tally(&ledger);
1041 ledger.frame([&Rectangle::new((0, 16).into(), (64, 16).into())]);
1042 ledger.coded(false, false);
1043 tally(&ledger);
1044 ledger.frame([]);
1045 ledger.cleaned();
1046 tally(&ledger);
1047 ledger.fit(32, 32);
1048 tally(&ledger);
1049 assert!(!ledger.coarse());
1050 }
1051
1052 #[test]
1055 fn a_refresh_follows_the_peaks_budget() {
1056 let mut pace = RefreshPace::default();
1057 let first = pace.most(25_600);
1058 assert_eq!(first, 100);
1059 pace.coded(20_000, 10_000.0, 25_600);
1060 assert_eq!(pace.most(25_600), 50);
1061 pace.coded(7_000, 10_000.0, 25_600);
1062 assert_eq!(pace.most(25_600), 50);
1063 pace.coded(1_000, 10_000.0, 25_600);
1064 assert_eq!(pace.most(25_600), 100);
1065 for _ in 0..7 {
1066 pace.coded(20_000, 10_000.0, 25_600);
1067 }
1068 assert_eq!(
1069 pace.most(25_600),
1070 0,
1071 "a block a frame past the budget waits"
1072 );
1073 for _ in 1..REFRESH_PAUSE {
1074 pace.most(25_600);
1075 }
1076 assert_eq!(pace.most(25_600), 1);
1077 }
1078
1079 #[test]
1080 fn a_resize_starts_the_ledger_over() {
1081 let mut ledger = BlockLedger::new(32, 32);
1082 ledger.frame([]);
1083 ledger.cleaned();
1084 ledger.fit(48, 32);
1085 assert_eq!(ledger.blocks(), 6);
1086 assert!(
1087 ledger.plan(0, 8, usize::MAX).is_some(),
1088 "every block unclean"
1089 );
1090 }
1091}