1use crate::engine::diagnostic::{Diagnostic, Severity, Value};
33use crate::engine::tiles::{Tile, TileCache, TileKey, QUALITY_BLOCK};
34use crate::kernels::geometry::{js_round, Rect};
35use crate::kernels::reveal::blocks::{BlockWrite, Deferred, Kind};
36use crate::kernels::reveal::run::{admitted, canvas_bytes, layer_bytes, Decision, Reveal};
37use crate::kernels::reveal::CanvasView;
38use std::collections::HashMap;
39
40pub use crate::kernels::reveal::run::Pass;
41
42pub const PASSES: [Pass; 4] = [Pass::Learn, Pass::Agree, Pass::Relearn, Pass::Write];
44pub const RING_FRAMES: usize = 3;
46pub const REVEALED_QUALITY: u8 = 204;
48
49#[derive(Clone, Debug, PartialEq)]
51pub struct RevealLayer {
52 pub id: String,
53 pub rect: Rect,
54 pub code: u32,
55}
56
57#[derive(Clone, Debug, PartialEq)]
59pub struct RevealCanvas {
60 pub id: String,
61 pub bounds: Rect,
62}
63
64#[derive(Clone, Debug, PartialEq)]
68pub struct ObservedDecision {
69 pub canvas: String,
70 pub layer: String,
71 pub x: f64,
72 pub y: f64,
73 pub skip: bool,
74 pub skipped: bool,
75 pub occlusions: Vec<Rect>,
76}
77
78#[derive(Clone, Copy, Debug, PartialEq, Eq)]
81pub enum MemoryStage {
82 Canvases,
83 Fallback,
84}
85
86impl MemoryStage {
87 pub fn as_str(self) -> &'static str {
89 match self {
90 MemoryStage::Canvases => "canvases",
91 MemoryStage::Fallback => "fallback",
92 }
93 }
94}
95
96#[derive(Clone, Debug, PartialEq)]
100pub struct Provenance {
101 pub canvas: String,
102 pub gx: i64,
103 pub gy: i64,
104 pub bw: usize,
105 pub bh: usize,
106 pub source: Vec<i32>,
107}
108
109#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
111pub struct Summary {
112 pub objects: u32,
113 pub components: u32,
114 pub coherent_blocks: u32,
115 pub component_blocks: u32,
116 pub provisional_blocks: u32,
117 pub fallback_blocks: u32,
118}
119
120#[derive(Clone, Debug, PartialEq)]
121pub enum Outcome {
122 Nothing,
124 Skipped(Diagnostic),
127 Revealed(Summary),
128}
129
130#[derive(Clone, Debug, PartialEq)]
131pub enum Step {
132 NeedMemory {
134 bytes: f64,
135 stage: MemoryStage,
136 },
137 NeedObservations,
139 NeedTile(TileKey),
141 Checkpoint,
143 Decode {
145 pass: Pass,
146 wanted: Vec<bool>,
147 },
148 NextFrame,
150 PassDone(Pass),
152 Provenance(Provenance),
154 Done(Outcome),
155}
156
157#[derive(Clone, Copy, Debug, PartialEq, Eq)]
158pub enum RevealError {
159 Unexpected,
163 NotProvided,
165 BadLayer,
167 BadLabels,
169}
170
171fn pixel_span(start: f64, size: f64) -> (i64, i64) {
173 let a = start.ceil();
174 (a as i64, ((start + size).ceil() - a).max(0.0) as i64)
175}
176
177fn block_tile(gx: i64, gy: i64, size: usize) -> (i32, i32, usize, usize) {
179 let (x0, y0, s) = (
180 gx * QUALITY_BLOCK as i64,
181 gy * QUALITY_BLOCK as i64,
182 size as i64,
183 );
184 (
185 x0.div_euclid(s) as i32,
186 y0.div_euclid(s) as i32,
187 x0.rem_euclid(s) as usize,
188 y0.rem_euclid(s) as usize,
189 )
190}
191
192fn write_block<'a>(
196 tile: &mut Tile,
197 size: usize,
198 (lx, ly): (usize, usize),
199 frame: u32,
200 kind: Kind,
201 row: impl Fn(usize) -> &'a [u8],
202 masked: bool,
203) {
204 let settled = matches!(kind, Kind::Coherent | Kind::Component | Kind::Edge);
205 for r in 0..QUALITY_BLOCK {
206 let src = row(r);
207 let p = (ly + r) * size + lx;
208 if !masked {
209 tile.pixels[p * 4..(p + QUALITY_BLOCK) * 4].copy_from_slice(&src[..QUALITY_BLOCK * 4]);
212 tile.coverage[p >> 3] = 0xff;
213 tile.coverage[(p >> 3) + 1] = 0xff;
214 if settled {
215 tile.provisional[p >> 3] = 0;
216 tile.provisional[(p >> 3) + 1] = 0;
217 }
218 continue;
219 }
220 for x in 0..QUALITY_BLOCK {
221 if src[x * 4 + 3] != 255 {
222 continue;
223 }
224 let q = p + x;
225 tile.pixels[q * 4..q * 4 + 3].copy_from_slice(&src[x * 4..x * 4 + 3]);
226 tile.pixels[q * 4 + 3] = 255;
227 tile.coverage[q >> 3] |= 1 << (q & 7);
228 if settled {
229 tile.provisional[q >> 3] &= !(1 << (q & 7));
230 }
231 }
232 }
233 let b = (ly / QUALITY_BLOCK) * (size / QUALITY_BLOCK) + lx / QUALITY_BLOCK;
234 if kind != Kind::Fallback {
236 tile.owner[b] = frame.wrapping_add(1);
237 }
238 if settled {
239 tile.conflicts[b] = 0;
240 tile.quality[b] = tile.quality[b].max(REVEALED_QUALITY);
241 }
242}
243
244enum Phase {
245 Start,
246 AwaitMemory {
248 stage: MemoryStage,
249 bytes: f64,
250 },
251 Refused(Diagnostic),
253 Observe,
255 AwaitObservations,
257 Ingest {
259 canvas: usize,
260 ty: i64,
261 tx: i64,
262 },
263 Begin {
265 pass: usize,
266 memory_checked: bool,
267 },
268 Fallbacks {
270 canvas: usize,
271 blocks: Vec<(i64, i64)>,
272 at: usize,
273 },
274 Decoding {
276 pass: usize,
277 },
278 Ending {
280 pass: usize,
281 },
282 Finish {
284 canvas: usize,
285 deferred: Option<Vec<Deferred>>,
286 at: usize,
287 },
288 Done,
289}
290
291struct Copies {
293 source: usize,
295 groups: Vec<(TileKey, Vec<BlockWrite>)>,
296 at: usize,
297}
298
299pub struct RevealSession {
301 width: usize,
302 height: usize,
303 noise: f64,
304 size: usize,
305 frames: u32,
306 layers: Vec<RevealLayer>,
307 canvases: Vec<RevealCanvas>,
308 layer_slot: HashMap<String, usize>,
309 canvas_slot: HashMap<String, usize>,
310 run: Option<Reveal>,
312 observed: Vec<(u32, Vec<(Decision, bool)>)>,
314 plans: Vec<Vec<Decision>>,
316 phase: Phase,
317 asked: Option<TileKey>,
319 slots: Vec<Vec<u8>>,
320 staged: Option<usize>,
322 last: Option<usize>,
324 before: Option<usize>,
325 pushed: bool,
326 copies: Option<Copies>,
327}
328
329impl RevealSession {
330 pub fn new(
333 width: usize,
334 height: usize,
335 noise: f64,
336 tile_size: usize,
337 frames: u32,
338 layers: Vec<RevealLayer>,
339 canvases: Vec<RevealCanvas>,
340 ) -> Result<RevealSession, RevealError> {
341 for l in &layers {
342 let (_, w) = pixel_span(l.rect.x, l.rect.width);
343 let (_, h) = pixel_span(l.rect.y, l.rect.height);
344 if w == 0 || h == 0 || l.code > 255 {
345 return Err(RevealError::BadLayer);
346 }
347 }
348 let layer_slot = layers
350 .iter()
351 .enumerate()
352 .map(|(i, l)| (l.id.clone(), i))
353 .collect();
354 let canvas_slot = canvases
355 .iter()
356 .enumerate()
357 .map(|(i, c)| (c.id.clone(), i))
358 .collect();
359 Ok(RevealSession {
360 width,
361 height,
362 noise,
363 size: tile_size,
364 frames,
365 layers,
366 canvases,
367 layer_slot,
368 canvas_slot,
369 run: None,
370 observed: Vec::new(),
371 plans: Vec::new(),
372 phase: Phase::Start,
373 asked: None,
374 slots: Vec::new(),
375 staged: None,
376 last: None,
377 before: None,
378 pushed: false,
379 copies: None,
380 })
381 }
382
383 pub fn estimate(&self) -> f64 {
385 let canvases: f64 = self
386 .canvases
387 .iter()
388 .map(|c| {
389 let (_, w) = pixel_span(c.bounds.x, c.bounds.width);
390 let (_, h) = pixel_span(c.bounds.y, c.bounds.height);
391 canvas_bytes(w as usize, h as usize) as f64
392 })
393 .sum();
394 let layers: f64 = self
395 .layers
396 .iter()
397 .map(|l| {
398 let (_, w) = pixel_span(l.rect.x, l.rect.width);
399 let (_, h) = pixel_span(l.rect.y, l.rect.height);
400 layer_bytes(w as usize, h as usize) as f64
401 })
402 .sum();
403 canvases + layers + (RING_FRAMES * self.width * self.height * 4) as f64
404 }
405
406 pub fn memory(&mut self, fits: bool, core_bytes: f64) -> Result<(), RevealError> {
409 let Phase::AwaitMemory { stage, bytes } = self.phase else {
410 return Err(RevealError::Unexpected);
411 };
412 if !fits {
413 let mb = js_round(bytes / 1048576.0) as f64;
414 let diagnostic = Diagnostic::new("REVEAL_SKIPPED", Severity::Warning)
415 .param("need", mb)
416 .detail(Value::Object(vec![
417 ("stage", Value::Text(stage.as_str().to_string())),
418 ("needMB", Value::Number(mb)),
419 (
420 "coreMB",
421 Value::Number(js_round(core_bytes / 1048576.0) as f64),
422 ),
423 ]));
424 self.phase = Phase::Refused(diagnostic);
425 return Ok(());
426 }
427 match stage {
428 MemoryStage::Canvases => {
429 let mut run = Reveal::new(self.width, self.height, self.noise);
430 for l in &self.layers {
431 let (x, w) = pixel_span(l.rect.x, l.rect.width);
432 let (y, h) = pixel_span(l.rect.y, l.rect.height);
433 run.add_layer(x, y, w as usize, h as usize, l.code as u8);
434 }
435 for c in &self.canvases {
436 let b = c.bounds;
438 run.add_canvas(
439 b.x as i32 as i64,
440 b.y as i32 as i64,
441 b.width as u32 as usize,
442 b.height as u32 as usize,
443 );
444 }
445 self.run = Some(run);
446 self.phase = Phase::Observe;
447 }
448 MemoryStage::Fallback => {
449 self.phase = Phase::Begin {
450 pass: 3,
451 memory_checked: true,
452 };
453 }
454 }
455 Ok(())
456 }
457
458 pub fn observation(
461 &mut self,
462 frame: u32,
463 decisions: &[ObservedDecision],
464 ) -> Result<(), RevealError> {
465 if !matches!(self.phase, Phase::AwaitObservations) {
466 return Err(RevealError::Unexpected);
467 }
468 let mut out = Vec::new();
469 for d in decisions {
470 let (Some(&canvas), Some(&layer)) = (
471 self.canvas_slot.get(&d.canvas),
472 self.layer_slot.get(&d.layer),
473 ) else {
474 continue;
475 };
476 if d.skipped && !d.skip {
477 continue;
478 }
479 let occlusions = d
480 .occlusions
481 .iter()
482 .map(|r| {
483 let (x, w) = pixel_span(r.x, r.width);
484 let (y, h) = pixel_span(r.y, r.height);
485 (x, y, w, h)
486 })
487 .collect();
488 out.push((
489 Decision {
490 layer,
491 canvas,
492 x: js_round(d.x) as i64,
493 y: js_round(d.y) as i64,
494 occlusions,
495 },
496 d.skipped,
497 ));
498 }
499 self.observed.push((frame, out));
500 Ok(())
501 }
502
503 pub fn frame_buffer(&mut self) -> Result<&mut [u8], RevealError> {
506 if !matches!(self.phase, Phase::Decoding { .. }) || self.copies.is_some() {
507 return Err(RevealError::Unexpected);
508 }
509 let slot = match self.staged {
510 Some(slot) => slot,
511 None => {
512 let held = |s: usize| self.last == Some(s) || self.before == Some(s);
513 let slot = match (0..self.slots.len()).find(|&s| !held(s)) {
514 Some(slot) => slot,
515 None => {
516 self.slots.push(vec![0; self.width * self.height * 4]);
517 self.slots.len() - 1
518 }
519 };
520 self.staged = Some(slot);
521 slot
522 }
523 };
524 Ok(&mut self.slots[slot])
525 }
526
527 fn check_labels(&self, labels: Option<&[u8]>) -> Result<(), RevealError> {
528 match labels {
529 Some(l) if l.len() != self.width * self.height => Err(RevealError::BadLabels),
530 _ => Ok(()),
531 }
532 }
533
534 pub fn push_frame(&mut self, index: u32, labels: Option<&[u8]>) -> Result<(), RevealError> {
537 let Phase::Decoding { pass } = self.phase else {
538 return Err(RevealError::Unexpected);
539 };
540 if self.copies.is_some() {
541 return Err(RevealError::Unexpected);
542 }
543 self.check_labels(labels)?;
544 let current = self.staged.take();
545 let run = self.run.as_mut().unwrap();
546 let slot = |s: Option<usize>| s.map(|s| &self.slots[s][..]);
547 let decisions = self.plans.get(index as usize).map_or(&[][..], |p| &p[..]);
548 run.frame(
549 index,
550 slot(current),
551 slot(self.last),
552 slot(self.before),
553 labels,
554 decisions,
555 );
556 if PASSES[pass] == Pass::Write {
557 if let Some(source) = self.last {
558 self.copies = Some(self.group(source));
559 }
560 }
561 self.before = self.last;
562 self.last = current;
563 self.pushed = true;
564 Ok(())
565 }
566
567 pub fn end_pass(&mut self, labels: Option<&[u8]>) -> Result<(), RevealError> {
569 let Phase::Decoding { pass } = self.phase else {
570 return Err(RevealError::Unexpected);
571 };
572 if self.copies.is_some() {
573 return Err(RevealError::Unexpected);
574 }
575 self.check_labels(labels)?;
576 self.staged = None;
577 if self.pushed {
578 let run = self.run.as_mut().unwrap();
579 let slot = |s: Option<usize>| s.map(|s| &self.slots[s][..]);
580 run.end(slot(self.last), slot(self.before), labels);
581 if PASSES[pass] == Pass::Write {
582 if let Some(source) = self.last {
583 self.copies = Some(self.group(source));
584 }
585 }
586 }
587 self.phase = Phase::Ending { pass };
588 Ok(())
589 }
590
591 fn group(&mut self, source: usize) -> Copies {
593 let writes = self.run.as_mut().unwrap().take_writes();
594 let mut groups: Vec<(TileKey, Vec<BlockWrite>)> = Vec::new();
595 let mut index: HashMap<TileKey, usize> = HashMap::new();
596 for w in writes {
597 let (tx, ty, _, _) = block_tile(w.gx, w.gy, self.size);
598 let key = TileKey::new(&self.canvases[w.canvas as usize].id, 0, tx, ty);
599 match index.get(&key) {
600 Some(&g) => groups[g].1.push(w),
601 None => {
602 index.insert(key.clone(), groups.len());
603 groups.push((key, vec![w]));
604 }
605 }
606 }
607 Copies {
608 source,
609 groups,
610 at: 0,
611 }
612 }
613
614 fn tile(
616 &mut self,
617 cache: &mut TileCache,
618 key: TileKey,
619 ) -> Result<Result<u32, Step>, RevealError> {
620 if let Some(id) = cache.touch(&key) {
621 self.asked = None;
622 return Ok(Ok(id));
623 }
624 if self.asked.as_ref() == Some(&key) {
625 return Err(RevealError::NotProvided);
626 }
627 self.asked = Some(key.clone());
628 Ok(Err(Step::NeedTile(key)))
629 }
630
631 fn apply_copies(
633 &mut self,
634 cache: &mut TileCache,
635 now: f64,
636 ) -> Result<Option<Step>, RevealError> {
637 while let Some(copies) = &self.copies {
638 let Some((key, _)) = copies.groups.get(copies.at) else {
639 self.copies = None;
640 break;
641 };
642 let id = match self.tile(cache, key.clone())? {
643 Ok(id) => id,
644 Err(step) => return Ok(Some(step)),
645 };
646 let copies = self.copies.as_mut().unwrap();
647 let (size, w) = (self.size, self.width);
648 let source = &self.slots[copies.source];
649 let tile = cache.get_mut(id).unwrap();
650 for wr in &copies.groups[copies.at].1 {
651 let (_, _, lx, ly) = block_tile(wr.gx, wr.gy, size);
652 let (sx, sy) = (wr.sx as usize, wr.sy as usize);
653 write_block(
654 tile,
655 size,
656 (lx, ly),
657 wr.frame,
658 wr.kind,
659 |row| {
660 let at = ((sy + row) * w + sx) * 4;
661 &source[at..at + QUALITY_BLOCK * 4]
662 },
663 false,
664 );
665 }
666 cache.mark_dirty(id, now);
667 copies.at += 1;
668 }
669 Ok(None)
670 }
671
672 pub fn step(&mut self, cache: &mut TileCache, now: f64) -> Result<Step, RevealError> {
675 loop {
676 match &mut self.phase {
677 Phase::Start => {
678 if self.layers.is_empty() || self.canvases.is_empty() {
679 self.phase = Phase::Done;
680 return Ok(Step::Done(Outcome::Nothing));
681 }
682 let bytes = self.estimate();
683 self.phase = Phase::AwaitMemory {
684 stage: MemoryStage::Canvases,
685 bytes,
686 };
687 return Ok(Step::NeedMemory {
688 bytes,
689 stage: MemoryStage::Canvases,
690 });
691 }
692 Phase::AwaitMemory { .. } | Phase::Done => return Err(RevealError::Unexpected),
693 Phase::Refused(_) => {
694 let Phase::Refused(d) = std::mem::replace(&mut self.phase, Phase::Done) else {
695 unreachable!()
696 };
697 self.run = None;
698 return Ok(Step::Done(Outcome::Skipped(d)));
699 }
700 Phase::Observe => {
701 self.phase = Phase::AwaitObservations;
702 return Ok(Step::NeedObservations);
703 }
704 Phase::AwaitObservations => {
705 self.plan();
706 self.phase = self.ingest_from(0);
707 }
708 Phase::Ingest { canvas, ty, tx } => {
709 let (slot, mut x, mut y) = (*canvas, *tx, *ty);
710 let Some(c) = self.canvases.get(slot) else {
711 self.phase = Phase::Begin {
712 pass: 0,
713 memory_checked: false,
714 };
715 continue;
716 };
717 let (b, s) = (c.bounds, self.size as f64);
718 if x as f64 * s >= b.x + b.width {
719 y += 1;
720 x = (b.x / s).floor() as i64;
721 }
722 if y as f64 * s >= b.y + b.height || x as f64 * s >= b.x + b.width {
724 self.phase = self.ingest_from(slot + 1);
725 continue;
726 }
727 self.phase = Phase::Ingest {
728 canvas: slot,
729 ty: y,
730 tx: x,
731 };
732 let key = TileKey::new(&self.canvases[slot].id, 0, x as i32, y as i32);
733 let id = match self.tile(cache, key)? {
734 Ok(id) => id,
735 Err(step) => return Ok(step),
736 };
737 let tile = cache.get(id).unwrap();
738 let size = self.size;
739 self.run.as_mut().unwrap().ingest(
740 slot,
741 &CanvasView {
742 x: x * size as i64,
743 y: y * size as i64,
744 width: size,
745 height: size,
746 rgba: &tile.pixels,
747 coverage: &tile.coverage,
748 },
749 );
750 self.phase = Phase::Ingest {
751 canvas: slot,
752 ty: y,
753 tx: x + 1,
754 };
755 return Ok(Step::Checkpoint);
756 }
757 Phase::Begin {
758 pass,
759 memory_checked,
760 } => {
761 let (pass, checked) = (*pass, *memory_checked);
762 let Some(&p) = PASSES.get(pass) else {
763 self.phase = Phase::Finish {
764 canvas: 0,
765 deferred: None,
766 at: 0,
767 };
768 continue;
769 };
770 let run = self.run.as_mut().unwrap();
771 if p == Pass::Write && !checked {
772 let bytes = run.fallback_bytes() as f64;
773 self.phase = Phase::AwaitMemory {
774 stage: MemoryStage::Fallback,
775 bytes,
776 };
777 return Ok(Step::NeedMemory {
778 bytes,
779 stage: MemoryStage::Fallback,
780 });
781 }
782 run.begin(p);
783 if p != Pass::Write {
784 return Ok(self.decode(pass));
785 }
786 self.phase = Phase::Fallbacks {
787 canvas: 0,
788 blocks: run.fallback_blocks(0),
789 at: 0,
790 };
791 }
792 Phase::Fallbacks { canvas, blocks, at } => {
793 let slot = *canvas;
794 let Some(&(gx, gy)) = blocks.get(*at) else {
795 if slot + 1 < self.canvases.len() {
796 let blocks = self.run.as_ref().unwrap().fallback_blocks(slot + 1);
797 self.phase = Phase::Fallbacks {
798 canvas: slot + 1,
799 blocks,
800 at: 0,
801 };
802 continue;
803 }
804 return Ok(self.decode(PASSES.len() - 1));
805 };
806 let (tx, ty, lx, ly) = block_tile(gx, gy, self.size);
807 let key = TileKey::new(&self.canvases[slot].id, 0, tx, ty);
808 let id = match self.tile(cache, key)? {
809 Ok(id) => id,
810 Err(step) => return Ok(step),
811 };
812 let tile = cache.get(id).unwrap();
813 let size = self.size;
814 let mut rgba = [0u8; QUALITY_BLOCK * QUALITY_BLOCK * 4];
815 for (row, out) in rgba.chunks_exact_mut(QUALITY_BLOCK * 4).enumerate() {
816 let p = (ly + row) * size + lx;
817 out.copy_from_slice(&tile.pixels[p * 4..(p + QUALITY_BLOCK) * 4]);
818 for x in 0..QUALITY_BLOCK {
819 let q = p + x;
820 if tile.coverage[q >> 3] & (1 << (q & 7)) == 0 {
821 out[x * 4 + 3] = 0;
822 }
823 }
824 }
825 self.run.as_mut().unwrap().set_main(slot, gx, gy, &rgba);
826 if let Phase::Fallbacks { at, .. } = &mut self.phase {
827 *at += 1;
828 }
829 }
830 Phase::Decoding { .. } => {
831 if let Some(step) = self.apply_copies(cache, now)? {
832 return Ok(step);
833 }
834 return Ok(Step::NextFrame);
835 }
836 Phase::Ending { pass } => {
837 let pass = *pass;
838 if let Some(step) = self.apply_copies(cache, now)? {
839 return Ok(step);
840 }
841 self.phase = Phase::Begin {
842 pass: pass + 1,
843 memory_checked: false,
844 };
845 return Ok(Step::PassDone(PASSES[pass]));
846 }
847 Phase::Finish {
848 canvas,
849 deferred,
850 at,
851 } => {
852 let slot = *canvas;
853 if slot >= self.canvases.len() {
854 self.phase = Phase::Done;
855 return Ok(Step::Done(Outcome::Revealed(self.summary())));
856 }
857 let run = self.run.as_ref().unwrap();
858 let list = deferred.get_or_insert_with(|| run.finish(slot));
859 let Some(d) = list.get(*at) else {
860 let provenance = run.provenance(slot, list);
861 self.phase = Phase::Finish {
862 canvas: slot + 1,
863 deferred: None,
864 at: 0,
865 };
866 if let Some((gx, gy, bw, bh, source)) = provenance {
867 return Ok(Step::Provenance(Provenance {
868 canvas: self.canvases[slot].id.clone(),
869 gx,
870 gy,
871 bw,
872 bh,
873 source,
874 }));
875 }
876 continue;
877 };
878 let (tx, ty, lx, ly) = block_tile(d.gx, d.gy, self.size);
879 let key = TileKey::new(&self.canvases[slot].id, 0, tx, ty);
880 let id = match self.tile(cache, key)? {
881 Ok(id) => id,
882 Err(step) => return Ok(step),
883 };
884 let Phase::Finish {
885 deferred: Some(list),
886 at,
887 ..
888 } = &mut self.phase
889 else {
890 unreachable!()
891 };
892 let d = &list[*at];
893 write_block(
894 cache.get_mut(id).unwrap(),
895 self.size,
896 (lx, ly),
897 d.frame,
898 d.kind,
899 |row| &d.rgba[row * QUALITY_BLOCK * 4..(row + 1) * QUALITY_BLOCK * 4],
900 true,
901 );
902 cache.mark_dirty(id, now);
903 *at += 1;
904 }
905 }
906 }
907 }
908
909 fn ingest_from(&self, canvas: usize) -> Phase {
911 match self.canvases.get(canvas) {
912 Some(c) => {
913 let s = self.size as f64;
914 Phase::Ingest {
915 canvas,
916 ty: (c.bounds.y / s).floor() as i64,
917 tx: (c.bounds.x / s).floor() as i64,
918 }
919 }
920 None => Phase::Begin {
921 pass: 0,
922 memory_checked: false,
923 },
924 }
925 }
926
927 fn decode(&mut self, pass: usize) -> Step {
929 self.staged = None;
930 self.last = None;
931 self.before = None;
932 self.pushed = false;
933 self.phase = Phase::Decoding { pass };
934 Step::Decode {
935 pass: PASSES[pass],
936 wanted: self.run.as_ref().unwrap().wanted(&self.plans),
937 }
938 }
939
940 fn plan(&mut self) {
942 let rows: Vec<Vec<(Decision, bool)>> =
943 self.observed.iter().map(|(_, ds)| ds.clone()).collect();
944 let keep = admitted(&rows);
945 let mut plans = vec![Vec::new(); self.frames as usize];
946 for ((frame, ds), keep) in std::mem::take(&mut self.observed).into_iter().zip(keep) {
947 if let Some(plan) = plans.get_mut(frame as usize) {
948 *plan = ds
949 .into_iter()
950 .zip(keep)
951 .filter(|(_, k)| *k)
952 .map(|((d, _), _)| d)
953 .collect();
954 }
955 }
956 self.plans = plans;
957 }
958
959 fn summary(&self) -> Summary {
960 let s = self.run.as_ref().unwrap().summary();
961 Summary {
962 objects: s.objects as u32,
963 components: s.components as u32,
964 coherent_blocks: s.coherent as u32,
965 component_blocks: s.component_blocks as u32,
966 provisional_blocks: s.provisional as u32,
967 fallback_blocks: s.fallback as u32,
968 }
969 }
970}
971
972#[cfg(test)]
973#[path = "reveal_tests.rs"]
974mod tests;