Skip to main content

pixelflux/
ledger.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5 */
6
7//! Where a region's picture stands, block by block: the per-block counterpart of the cleanup
8//! policy's per-region bookkeeping (`pipeline::cleanup_due`).
9//!
10//! The cleanup policy reads a region as still or moving whole, so a region that never holds
11//! still (a video playing in one window) keeps everything else in it at the quality motion is
12//! coded at: 1080p text beside a 640x360 video at 8 Mbit/s stayed at 32 dB, where the same
13//! text alone was cleaned up to 55. The ledger keeps, for each `BLOCK` x `BLOCK` block, how
14//! many frames it has held still and whether its last coding was at the paint-over quality
15//! (clean). The capture's damage marks the blocks it covers changed and unclean each frame; a
16//! frame that codes blocks finer through the encoder's per-block quantizer
17//! (`FrameEncoder::refine_blocks`, x264's quantizer offsets) asks the ledger which
18//! (`BlockLedger::plan`): blocks that held still for the cleanup's trigger and are not clean,
19//! at most so many a frame, taken in turn from where the last plan stopped so a large set is
20//! refined across frames rather than in one, and marks them clean once coded. A whole frame
21//! coded at the paint-over quality cleans every block it did not see change; a key frame at the
22//! rate control's quality leaves every block unclean. A block that held still and is clean is
23//! constant (`BlockLedger::constant`): a frame of the rate control's may code it as a skip
24//! without analysis (`FrameEncoder::constant_blocks`), since its reference already holds it at
25//! the cleanup's quality. A block coded while a capped constant quality's peak bound it is
26//! coarse (`BlockLedger::coded`), never clean; once the peak lets go, the coarse blocks that held
27//! still are coded again at the session's own quality, so many a frame (`BlockLedger::refresh`,
28//! `RefreshPace`), the rest of them waiting as skips. Nothing reads pixels: the damage source
29//! did the detection, and a source that over-reports (a client damaging its whole surface)
30//! only refines less, never wrongly.
31
32use smithay::utils::{Physical, Rectangle};
33
34/// The side of a block in pixels: an H.264 or VP8 macroblock. An encoder whose quantizer map is
35/// coarser (a 32x32 HEVC tree block, a 64x64 AV1 superblock) reads its unit from the blocks it
36/// covers (`BlockPlan::refines_area`).
37pub const BLOCK: i32 = 16;
38
39/// Quality index steps finer than the paint-over one x264 codes a refined block at, so the block
40/// comes to what the region's own cleanup reaches: one predicted frame at the paint-over
41/// quantizer left 1080p text at 44.6 dB, where the cleanup's key frame, at x264's intra offset,
42/// reached 49.1; six steps finer reached 49.4. Each encoder that refines says its own
43/// (`FrameEncoder::refines`).
44pub const REFINE_FINER: u32 = 6;
45
46/// The most blocks a region refines a frame, as a share of it: a large set is refined over
47/// `REFINE_FRAMES` frames at least rather than in one frame the size of a cleanup. A constant
48/// quality refines that many; a constant rate starts at a share of `REFINE_START_FRAMES` and
49/// follows its rate control (`RefinePace`).
50const REFINE_FRAMES: usize = 64;
51const REFINE_START_FRAMES: usize = 512;
52
53/// Quality index steps the rate control's frames may come out coarser than before the region
54/// started refining before its refined blocks a frame halve (or past `REFINE_OVER`); where they
55/// come out no coarser, a quarter more. Dense 1080p text refined at 128 blocks a frame beside a
56/// video at 8 Mbit/s ran x264's row-level control past quantizer 51, where it scales every
57/// macroblock offset toward nothing, and left the text at 42 dB; at 16 a frame it reached 48.
58const REFINE_RISE: i32 = 2;
59
60/// Frame budgets, or as much as the frames before refining came to where that is more, a
61/// refining frame may come out at before its refined blocks a frame halve, as they do past
62/// `REFINE_RISE`: a rate control pinned at its coarsest quantizer says nothing through it, and
63/// at 2 Mbit/s at 1080p x264 sent the refinement at twice the rate.
64const REFINE_OVER: f64 = 1.5;
65
66/// How an encoder refines blocks (`FrameEncoder::refines`): the quality index steps finer than
67/// the paint-over one it codes them at, so a refined block comes to what the region's own
68/// cleanup reaches (`finer`); the most steps off the frame's own quality one frame may code a
69/// block at (`reach`), past which its rate control loses the rate; and the most runs of blocks
70/// along a row one frame may refine (`runs`), where the encoder takes regions rather than a map.
71#[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    /// A refiner `finer` steps past the paint-over quality, with no bound on its reach or runs.
80    pub const fn finer(finer: u32) -> Self {
81        Self {
82            finer,
83            reach: u32::MAX,
84            runs: usize::MAX,
85        }
86    }
87}
88
89/// A frame's refinement: the blocks (`plan`), the quality index they are coded at (`target`),
90/// the rate control's index the offset to it was taken from (`from`), and the most steps the
91/// offset may take (`reach`, `Refiner::reach`).
92#[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    /// The quality index steps the refined blocks are coded off the frame's own.
102    pub fn offset(&self) -> i32 {
103        (self.target - self.from).clamp(-(self.reach.min(i32::MAX as u32) as i32), 0)
104    }
105}
106
107/// Frames a region refines nothing for once a frame refining one block came out past
108/// `REFINE_OVER` or `REFINE_RISE`: a rate that cannot afford a block a frame is one where the
109/// encoder's own refinement of the region does better than a block a frame. VP8 at 2 Mbit/s
110/// beside a video came to 24.2 dB refining a block a frame, against 29.6 with none, its cyclic
111/// refresh, which the region map displaces, refining the still screen itself.
112const REFINE_PAUSE: u32 = 64;
113
114/// The frame budgets the frames of a region refining nothing may average before it refines
115/// nothing for `REFINE_PAUSE` frames: a rate control that already runs over its rate has none to
116/// spare. Beside a video, VP8's frames averaged 1.38 budgets at 2 Mbit/s, where refining took
117/// the text to 25.5 dB against 29.6 by its cyclic refresh and 12% more bytes, and 0.76 at 8
118/// Mbit/s, where it took the text from 33.5 to 42.6.
119const REFINE_RATE_HELD: f64 = 1.2;
120
121/// How many blocks a region refines a frame (`REFINE_FRAMES`, `REFINE_RISE`, `REFINE_OVER`,
122/// `REFINE_PAUSE`, `REFINE_RATE_HELD`).
123#[derive(Clone, Debug, Default)]
124pub struct RefinePace {
125    count: usize,
126    base: Option<i32>,
127    /// The bytes of the frames that refined nothing, on average over the last eight or so.
128    base_bytes: f64,
129    paused: u32,
130}
131
132impl RefinePace {
133    /// The most blocks of a region of `blocks` this frame refines, under a constant rate or not.
134    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    /// A frame the rate control coded at quality index `coded` in `bytes` against a `budget`,
150    /// which refined blocks or not, of a region of `blocks`.
151    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
178/// Quality index steps finer than the session's own a capped constant quality's refresh codes its
179/// coarse blocks at (`BlockLedger::refresh`, `Codec::refresh_index`), so a predicted frame
180/// refreshing them comes to what the uncapped session's picture stands at: refreshing a 720p
181/// page of text a scroll left coarse, libvpx's VP8 and VP9 came to 44.8 and 41.7 dB against the
182/// uncapped sessions' 44.5 and 40.5, and NVENC's H.264 to 41.1 against 38.8. x264 takes a step
183/// more (`X264_REFRESH_FINER` in the software encoder).
184pub const REFRESH_FINER: u32 = 3;
185
186/// Quality index steps further finer a refresh codes a block a capped key frame left coarse at
187/// (`BlockLedger::refresh`), where the uncapped session's key frame stood at its rate factor's
188/// intra offset finer than its predicted frames: x264's coarse start on a 1080p desktop left
189/// the screen's still parts 0.6 dB under the uncapped session's refreshed `REFRESH_FINER` steps
190/// finer.
191pub const REFRESH_KEY_FINER: u32 = 3;
192
193/// Frames a capped constant quality's refresh of its coarse blocks (`BlockLedger::refresh`) would
194/// take at its first share of the region's blocks a frame, which halves while a refreshing frame
195/// comes out past the peak's frame budget and doubles while one comes out under half of it
196/// (`RefreshPace`): on a fast path the share doubles to the whole region in eight frames, and
197/// on a slow one the first frame is a small one.
198const REFRESH_FRAMES: usize = 256;
199
200/// Frames a refresh waits, refreshing nothing and holding nothing back, once a frame refreshing
201/// one block comes out past the peak's frame budget: the content alone fills the peak, as where
202/// NVENC runs past a peak it leaves its level for, and a refresh crawling a block a frame would
203/// hold the rest of the coarse blocks back for seconds, which NVENC holds at its coarsest
204/// quantizer through its QP map and deblocks frame after frame (the static parts of a 1080p
205/// scene 0.4 dB under the uncapped session's after two seconds).
206const REFRESH_PAUSE: u32 = 60;
207
208/// How many coarse blocks a region refreshes a frame (`REFRESH_FRAMES`, `REFRESH_PAUSE`).
209#[derive(Clone, Debug, Default)]
210pub struct RefreshPace {
211    count: usize,
212    paused: u32,
213}
214
215impl RefreshPace {
216    /// The most blocks of a region of `blocks` this frame refreshes, 0 while it waits.
217    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    /// A refreshing frame came out at `bytes` against the peak's frame `budget`.
229    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/// The blocks of a picture to code at the paint-over quality this frame (`BlockLedger::plan`).
245#[derive(Clone, Debug, PartialEq)]
246pub struct BlockPlan {
247    pub cols: usize,
248    pub rows: usize,
249    refine: Vec<u64>,
250    /// How many blocks the plan refines.
251    pub count: usize,
252}
253
254impl BlockPlan {
255    /// A plan of this one's blocks that refines none of them.
256    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    /// Whether block (`col`, `row`) is refined.
266    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    /// Whether any refined block lies in the pixel area from (`x`, `y`) of `width` x `height`:
272    /// an encoder whose map unit spans several blocks refines a unit that holds one.
273    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/// Per-block stillness and cleanliness of one region (module docblock).
283#[derive(Clone, Debug, Default)]
284pub struct BlockLedger {
285    width: i32,
286    height: i32,
287    cols: usize,
288    rows: usize,
289    /// Frames each block has held still, saturating.
290    still: Vec<u16>,
291    /// Whether each block's last coding was at the paint-over quality or finer.
292    clean: Vec<bool>,
293    /// Whether each block was refined (`refined`) since it last changed: coded at the target a
294    /// key-frame cleanup reaches, which a refresh at the paint-over quality does not.
295    refined: Vec<bool>,
296    /// Whether each block was last coded under a capped constant quality's binding peak,
297    /// coarser than the rate factor would have chosen (`coded`), and not refreshed since, and
298    /// whether that was in a key frame.
299    coarse: Vec<bool>,
300    coarse_key: Vec<bool>,
301    /// How many blocks are coarse, so a session no peak ever bound reads none at no cost.
302    coarse_count: usize,
303    /// Whether each block is owed its paint-over: a cleanup that fell due on it was left to the
304    /// refresh, or coded short of its target under a binding peak (`spent`), and the block not
305    /// coded at it since. An owed block is coarse too, and the refresh codes it at the
306    /// paint-over quality (`refresh_owed`).
307    owed: Vec<bool>,
308    owed_count: usize,
309    /// Whether each block was refreshed at the session's own quality since it changed: a
310    /// cleanup coded over it as over the picture an uncapped session holds leaves it coarser
311    /// than that session's paint-over, the quality it starts from being finer (`fresh`).
312    fresh: Vec<bool>,
313    fresh_count: usize,
314    /// The block the next refresh starts its turn from.
315    next_refresh: usize,
316    /// The block the next plan starts its turn from.
317    next: usize,
318    /// Whether the region's source was last seen to leave a change unreported
319    /// (`frame_reported`).
320    unreported: bool,
321}
322
323impl BlockLedger {
324    /// A ledger of a `width` x `height` region, every block changed and unclean.
325    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    /// Follow the region to `width` x `height`, starting over where that changed it: a resize,
350    /// a scale change, or a rebuilt encoder leaves nothing of the old picture's quality.
351    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    /// One frame of the region: the blocks `rects` cover changed and are unclean, and every
358    /// other block held still one more frame. Damage across frames the capture skipped is the
359    /// caller's to union into `rects`.
360    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    /// One frame of a region whose source reports where it changed (`rects`) and whose content
381    /// hash says whether it did (`dirty`): the blocks `rects` cover changed, and where none does
382    /// the whole region did, while the source was last seen to leave a change unreported. A
383    /// frame the hash checked and found changed (`observed`) says which: without a rectangle
384    /// the source missed it, as the X server's DAMAGE misses directly rendered GL. A hash that
385    /// keeps a region dirty for a run of frames after it moved checks only the run's last, so
386    /// read as a change, the frames after a video stopped beside clean text dropped every block
387    /// the ledger had refined.
388    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    /// The whole region changed this frame (a source that cannot say where).
405    pub fn frame_all(&mut self) {
406        self.still.fill(0);
407        self.clean.fill(false);
408        self.refined.fill(false);
409    }
410
411    /// The frame just coded the whole region at the rate control's quality where it was clean
412    /// (a key frame a consumer asked for): every block is unclean again.
413    pub fn coarsened(&mut self) {
414        self.clean.fill(false);
415        self.refined.fill(false);
416    }
417
418    /// The frame just coded the whole region at the paint-over quality or finer: every block
419    /// that did not change in it is clean, and no longer coarse nor owed its paint-over.
420    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    /// Block `i` holds no refresh at the session's own quality.
439    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    /// A cleanup that fell due on the region is left to the refresh, or was coded short of its
447    /// target under a binding peak: every block that did not change this frame is owed its
448    /// paint-over, for the refresh once the peak lets go (`refresh_owed`). Whether that owed
449    /// any block it did not owe.
450    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    /// Whether every block that held still is clean: nothing for a cleanup to do.
472    pub fn settled(&self) -> bool {
473        self.clean
474            .iter()
475            .zip(&self.still)
476            .all(|(&c, &s)| c || s == 0)
477    }
478
479    /// The blocks to refine this frame: those still for `trigger` frames or more and not
480    /// clean, at most `max` in at most `runs` runs along a row, in turn from where the last
481    /// plan stopped; `None` where there are none.
482    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    /// A frame that carried `refinement` came out with the rate control at quality index
521    /// `coded`: its blocks are clean where that put them at the target or finer. A rate control
522    /// codes the rows a refinement overspends coarser, and a block it left short is refined again
523    /// on its next turn, from the index it now codes at.
524    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    /// The blocks `plan` refined were coded at the paint-over quality: they are clean.
532    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    /// The share of the region's blocks not refined since they last changed: what a key-frame
544    /// cleanup has left to do (`pipeline::cleanup_due`), the whole region on a session that does
545    /// not refine.
546    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    /// Whether each block, in raster order, held still this frame: what the source reported
555    /// unchanged since the frame before.
556    pub fn unchanged(&self) -> impl Iterator<Item = bool> + '_ {
557        self.still.iter().map(|&s| s > 0)
558    }
559
560    /// Whether each block, in raster order, held still this frame and is clean: coded at the
561    /// cleanup's quality since it last changed, so a frame of the rate control's may code it as
562    /// a skip without analysis (`FrameEncoder::constant_blocks`). A still block not yet clean is
563    /// left to the encoder, whose rate control refines it as it codes the frame; a skip there
564    /// would hold it at the quality it was coded at while moving.
565    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    /// The number of blocks in the region.
573    pub fn blocks(&self) -> usize {
574        self.cols * self.rows
575    }
576
577    /// The frame just coded the blocks that changed in it, or the whole region where it was a
578    /// key frame (`whole`), under a capped constant quality's binding peak (`capped`): coarser
579    /// than the rate factor would have chosen, so coarse and unclean, or at the session's own
580    /// quality, so no longer coarse. Either way they are owed no paint-over: one falls due on
581    /// them again once they hold still.
582    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    /// The coarse blocks (`coded`) that held still this frame, split into the ones it codes
609    /// again at the session's own quality, at most `max` in turn from where the last refresh
610    /// stopped, and the rest, which wait as skips; `None` where none is coarse.
611    pub fn refresh(&mut self, max: usize) -> Option<(BlockPlan, BlockPlan)> {
612        self.refresh_keyed(max).map(|(now, _, wait)| (now, wait))
613    }
614
615    /// `refresh`, with the blocks it codes now that a capped key frame left coarse apart
616    /// (`REFRESH_KEY_FINER`), among them. A block owed its paint-over waits for
617    /// `refresh_owed`.
618    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    /// The coarse blocks owed their paint-over (`spent`) that held still this frame, split into
656    /// the ones it codes again at the paint-over quality, at most `max` in turn from where the
657    /// last refresh stopped, and every other coarse block that held still, which waits as a
658    /// skip; `None` where none is owed. A refresh takes these before the rest (`refresh`).
659    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    /// The blocks `plan` refreshed were coded at the session's own quality, or at the
692    /// paint-over quality where they were owed it (`refresh_owed`), which leaves them clean: no
693    /// longer coarse.
694    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    /// Whether any block is owed its paint-over (`spent`).
719    pub fn owed(&self) -> bool {
720        self.owed_count > 0
721    }
722
723    /// Whether any block was refreshed at the session's own quality since it changed.
724    pub fn fresh(&self) -> bool {
725        self.fresh_count > 0
726    }
727
728    /// Whether any block is coarse (`coded`).
729    pub fn coarse(&self) -> bool {
730        self.coarse_count > 0
731    }
732}
733
734#[cfg(test)]
735mod tests {
736    use super::*;
737
738    /// A frame the content hash keeps dirty without a rectangle drops nothing a source that
739    /// reports its changes refined; a change the hash saw and the source did not report drops it
740    /// all, and so does every dirty frame without a rectangle after it.
741    #[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    /// A refresh at the paint-over quality cleans the blocks that held still without refining
760    /// them: a key-frame cleanup still has them to do.
761    /// A block is constant once it held still and was cleaned; a change makes it variable
762    /// again, and a still block the cleanup has not reached stays variable.
763    #[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    /// The blocks a capped frame coded are coarse and unclean; once the peak lets go, the ones
922    /// that held still are refreshed so many a frame in turn, the rest waiting, each once, and
923    /// a block an uncapped frame codes again is no longer coarse.
924    #[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    /// A cleanup coded under a binding peak leaves the blocks that held still owed their
968    /// paint-over: a refresh codes them first, so many a frame, every other coarse block waiting,
969    /// and leaves them clean; a block that changed on the cleanup frame is owed nothing.
970    #[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    /// The count of coarse blocks follows the blocks through every path that marks or clears
1007    /// them, a resize among them.
1008    #[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    /// A refresh spreads over more frames while its frames come out past the peak's budget, and
1053    /// over fewer while they come out well under it; one a block a frame still past it waits.
1054    #[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}