1mod region;
26
27use crate::engine::attachments::{Attachment, Attachments};
28use crate::engine::diagnostic::{Diagnostic, Severity};
29use crate::engine::keyframes::{KeyframeIndex, Retrieval};
30use crate::engine::pose_graph::{PoseGraph, PoseGraphError, Write};
31use crate::kernels::features::{extract_features, Feature};
32use crate::kernels::geometry::Rect;
33use crate::kernels::raster::{downscale_gray, downscaled_size, grayscale};
34use crate::kernels::region::{Mask, Region as Shape};
35use crate::kernels::voting::{Finalized, Ring};
36use std::collections::HashMap;
37use std::fmt;
38use std::sync::Arc;
39
40pub const VOTING_RING_BYTES: usize = 24 * 1024 * 1024;
42pub const FALLBACK_FEATURES: usize = 480;
44pub const PATCH_COUNT: usize = 24;
46pub const PATCH_SIZE: usize = 32;
47pub const POSE_BOUND: f64 = crate::engine::compositor::CANVAS_PIXEL_BOUND - 65536.0;
49
50pub fn is_valid_pose(x: f64, y: f64) -> bool {
52 x.is_finite() && y.is_finite() && x.abs() < POSE_BOUND && y.abs() < POSE_BOUND
53}
54
55pub(super) const UNREGISTERED: u8 = 255;
57
58pub struct SolveRegion {
60 pub id: String,
61 pub name: String,
62 pub moving: bool,
64 pub kind: String,
66 pub unassigned: bool,
67 pub code: u8,
69 pub shape: Shape,
70}
71
72pub struct SolveConfig {
73 pub width: usize,
74 pub height: usize,
75 pub factor: u32,
77 pub radius: i32,
79 pub noise: f64,
81 pub voting_budget: usize,
82 pub regions: Vec<SolveRegion>,
83}
84
85#[derive(Clone, Copy, Debug, PartialEq)]
87pub struct ScanFields {
88 pub duplicate: bool,
89 pub zoom: f64,
90 pub difference: f64,
91}
92
93#[derive(Clone, Debug, PartialEq)]
95pub struct Plane {
96 pub width: usize,
97 pub height: usize,
98 pub data: Vec<u8>,
99}
100
101#[derive(Clone, Debug, PartialEq)]
103pub struct KeyPatch {
104 pub x: f64,
105 pub y: f64,
106 pub size: usize,
107 pub data: Vec<u8>,
108}
109
110#[derive(Clone, Debug, PartialEq)]
112pub struct Keyframe {
113 pub id: String,
114 pub node: String,
115 pub canvas_id: String,
116 pub layer: String,
117 pub frame: u32,
118 pub features: Vec<Feature>,
119 pub gray: Arc<Plane>,
120 pub x: f64,
121 pub y: f64,
122 pub scale_x: f64,
123 pub scale_y: f64,
124 pub patches: Vec<KeyPatch>,
125}
126
127#[derive(Clone, Debug, PartialEq)]
129pub struct CanvasMeta {
130 pub id: String,
131 pub layer: String,
132 pub name: String,
133 pub kind: String,
134 pub fragment: u32,
135 pub first_time: f64,
136 pub last_time: f64,
137 pub attached_to: Option<String>,
138}
139
140#[derive(Clone, Debug, PartialEq)]
142pub struct Placement {
143 pub layer: String,
144 pub canvas_id: String,
145 pub node: String,
146 pub x: f64,
147 pub y: f64,
148 pub confidence: f64,
149 pub uncertain: bool,
150 pub time: f64,
151 pub skip: bool,
152 pub occlusions: Vec<Rect>,
153}
154
155pub type FragmentScale = Option<(f64, f64)>;
157
158#[derive(Clone, Debug, PartialEq)]
160pub enum Effect {
161 Canvas {
163 meta: CanvasMeta,
164 created: bool,
165 },
166 Attachment(Attachment),
168 Keyframe(Arc<Keyframe>),
170 Graph(Write),
172 Diagnostic(Diagnostic),
173}
174
175#[derive(Clone, Debug, PartialEq)]
177pub struct VotingVerdict {
178 pub region: String,
179 pub x0: i32,
180 pub y0: i32,
181 pub w: i32,
182 pub h: i32,
183 pub bits: Vec<u8>,
184 pub clean: Vec<u8>,
185}
186
187#[derive(Clone, Debug, PartialEq)]
189pub struct VotingRecord {
190 pub index: u32,
191 pub verdicts: Vec<VotingVerdict>,
192 pub voted_layers: u32,
193 pub thin_layers: u32,
194}
195
196#[derive(Clone, Debug, PartialEq)]
198pub struct FrameDone {
199 pub placements: Vec<Placement>,
201 pub joined_ring: bool,
204 pub voting: Vec<VotingRecord>,
205}
206
207#[derive(Clone, Debug, PartialEq)]
209pub enum Begin {
210 Duplicate(Vec<Placement>),
212 NeedFrame { gray: bool },
215}
216
217#[derive(Clone, Debug, PartialEq)]
218pub enum Poll {
219 NeedKeyframes(Vec<String>),
221 Done(FrameDone),
222}
223
224#[derive(Clone, Debug, PartialEq)]
225pub enum SolveError {
226 Graph(PoseGraphError),
227 BadInput(&'static str),
229}
230
231impl fmt::Display for SolveError {
232 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233 match self {
234 SolveError::Graph(e) => e.fmt(f),
235 SolveError::BadInput(what) => write!(f, "CORE_BAD_ARGUMENT: {what}."),
236 }
237 }
238}
239
240impl std::error::Error for SolveError {}
241
242impl From<PoseGraphError> for SolveError {
243 fn from(e: PoseGraphError) -> Self {
244 SolveError::Graph(e)
245 }
246}
247
248struct RegionState {
250 phase_support: bool,
253 registration_support: Option<Rect>,
254 screen_fixed: Option<crate::kernels::track::ScreenFixed>,
256 canvas_id: String,
257 fragment: u32,
258 pose: (f64, f64),
259 velocity: (f64, f64),
261 last_node: Option<(String, u32)>,
263 anchor: Option<Arc<Keyframe>>,
264 previous_features: Option<Vec<Feature>>,
265 blind: bool,
267 weak: bool,
269 started: bool,
271}
272
273struct FrameWork {
275 index: u32,
276 time: f64,
277 scan: ScanFields,
278 gray: Arc<Plane>,
279 features: Arc<Vec<Feature>>,
280 placements: Vec<Placement>,
281 region: usize,
283 step: Option<Box<region::Step>>,
284 joined_ring: bool,
285}
286
287enum Stage {
288 Idle,
289 AwaitFrame {
290 index: u32,
291 time: f64,
292 scan: ScanFields,
293 },
294 Frame(Box<FrameWork>),
295}
296
297pub struct SolveSession {
298 width: usize,
299 height: usize,
300 factor: u32,
301 radius: i32,
302 analysis: (usize, usize),
303 regions: Vec<SolveRegion>,
304 states: Vec<RegionState>,
305 voting_slot: Vec<Option<usize>>,
307 voting: Ring,
308 canvas_slots: HashMap<String, u32>,
309 graph: PoseGraph,
310 index: KeyframeIndex,
311 attachments: Attachments,
312 canvases: HashMap<String, CanvasMeta>,
313 frames: [Vec<u8>; 2],
314 previous_slot: Option<usize>,
316 current_slot: usize,
317 native: Vec<u8>,
318 native_filled: bool,
319 previous_gray: Option<Arc<Plane>>,
320 previous_features: Option<Arc<Vec<Feature>>>,
321 previous_plan: Option<Vec<Placement>>,
322 registration_labels: Option<(Arc<Vec<u8>>, Vec<u8>)>,
325 registration_stale: bool,
326 stage: Stage,
327 effects: Vec<Effect>,
328 #[cfg(test)]
330 seams: tests::Seams,
331}
332
333fn clone_shape(s: &Shape) -> Shape {
334 Shape {
335 rect: s.rect,
336 exclusions: s.exclusions.clone(),
337 crop: s.crop,
338 solid: s.solid,
339 mask: s.mask.as_ref().map(|m| Mask {
340 width: m.width,
341 height: m.height,
342 factor: m.factor,
343 data: m.data.clone(),
344 }),
345 }
346}
347
348fn stored(mut f: Feature) -> Feature {
350 f.score = f.score as f32 as f64;
351 f
352}
353
354impl SolveSession {
355 pub fn new(config: SolveConfig) -> SolveSession {
356 let SolveConfig {
357 width,
358 height,
359 factor,
360 radius,
361 noise,
362 voting_budget,
363 regions,
364 } = config;
365 let factor = factor.max(1);
366 let analysis = downscaled_size(width, height, factor as usize);
367 let mut voting_slot = Vec::with_capacity(regions.len());
368 let mut voting_regions = Vec::new();
369 for r in ®ions {
370 if r.moving {
371 voting_slot.push(Some(voting_regions.len()));
372 voting_regions.push(clone_shape(&r.shape));
373 } else {
374 voting_slot.push(None);
375 }
376 }
377 let voting = Ring::new(
378 factor as i32,
379 noise,
380 width,
381 height,
382 analysis.0,
383 analysis.1,
384 voting_budget,
385 voting_regions,
386 );
387 let states = regions
388 .iter()
389 .map(|r| RegionState {
390 phase_support: false,
391 registration_support: None,
392 screen_fixed: r.moving.then(|| {
393 let rect = r.shape.rect;
394 let f = factor as f64;
395 let (x0, y0) = ((rect.x / f).floor(), (rect.y / f).floor());
396 crate::kernels::track::ScreenFixed::new(
397 x0 as i64,
398 y0 as i64,
399 (((rect.x + rect.width) / f).ceil() - x0).max(0.0) as usize,
400 (((rect.y + rect.height) / f).ceil() - y0).max(0.0) as usize,
401 )
402 }),
403 canvas_id: String::new(),
404 fragment: 0,
405 pose: (0.0, 0.0),
406 velocity: (0.0, 0.0),
407 last_node: None,
408 anchor: None,
409 previous_features: None,
410 blind: false,
411 weak: false,
412 started: false,
413 })
414 .collect();
415 SolveSession {
416 width,
417 height,
418 factor,
419 radius,
420 analysis,
421 regions,
422 states,
423 voting_slot,
424 voting,
425 canvas_slots: HashMap::new(),
426 graph: PoseGraph::new(),
427 index: KeyframeIndex::new(),
428 attachments: Attachments::new(),
429 canvases: HashMap::new(),
430 frames: [vec![0; width * height * 4], vec![0; width * height * 4]],
431 previous_slot: None,
432 current_slot: 0,
433 native: vec![0; width * height],
434 native_filled: false,
435 previous_gray: None,
436 previous_features: None,
437 previous_plan: None,
438 registration_labels: None,
439 registration_stale: false,
440 stage: Stage::Idle,
441 effects: Vec::new(),
442 #[cfg(test)]
443 seams: tests::Seams::default(),
444 }
445 }
446
447 pub fn width(&self) -> usize {
448 self.width
449 }
450
451 pub fn height(&self) -> usize {
452 self.height
453 }
454
455 pub fn analysis_size(&self) -> (usize, usize) {
457 self.analysis
458 }
459
460 pub fn graph(&self) -> &PoseGraph {
462 &self.graph
463 }
464
465 pub fn graph_mut(&mut self) -> &mut PoseGraph {
466 &mut self.graph
467 }
468
469 pub fn take_graph(&mut self) -> PoseGraph {
471 std::mem::take(&mut self.graph)
472 }
473
474 pub fn take_effects(&mut self) -> Vec<Effect> {
476 std::mem::take(&mut self.effects)
477 }
478
479 pub fn begin_frame(
481 &mut self,
482 index: u32,
483 time: f64,
484 scan: ScanFields,
485 ) -> Result<Begin, SolveError> {
486 if !matches!(self.stage, Stage::Idle) {
487 return Err(SolveError::BadInput("a frame is already in progress"));
488 }
489 if scan.duplicate && self.states.iter().all(|s| !s.blind) {
490 if let Some(previous) = &mut self.previous_plan {
491 for p in previous.iter_mut() {
492 p.time = time;
493 }
494 let placements = previous.clone();
495 for s in &mut self.states {
496 s.velocity = (0.0, 0.0);
497 }
498 return Ok(Begin::Duplicate(placements));
499 }
500 }
501 self.current_slot = match self.previous_slot {
502 Some(p) => 1 - p,
503 None => 0,
504 };
505 self.stage = Stage::AwaitFrame { index, time, scan };
506 Ok(Begin::NeedFrame {
507 gray: !(scan.duplicate && self.previous_gray.is_some()),
508 })
509 }
510
511 pub fn frame_mut(&mut self) -> &mut [u8] {
513 &mut self.frames[self.current_slot]
514 }
515
516 pub fn run_frame(
520 &mut self,
521 gray: Option<Vec<u8>>,
522 features: Option<Vec<Feature>>,
523 labels: &[u8],
524 ) -> Result<Poll, SolveError> {
525 let Stage::AwaitFrame { index, time, scan } =
526 std::mem::replace(&mut self.stage, Stage::Idle)
527 else {
528 return Err(SolveError::BadInput("no frame was asked for"));
529 };
530 if labels.len() != self.width * self.height {
531 return Err(SolveError::BadInput(
532 "the label plane does not match the frame",
533 ));
534 }
535 let (aw, ah) = self.analysis;
536 let gray = match (scan.duplicate, &self.previous_gray, gray) {
537 (true, Some(previous), _) => previous.clone(),
538 (_, _, Some(data)) => {
539 if data.len() != aw * ah {
540 return Err(SolveError::BadInput(
541 "analysis gray size differs from the run",
542 ));
543 }
544 Arc::new(Plane {
545 width: aw,
546 height: ah,
547 data,
548 })
549 }
550 _ => {
551 let mut data = vec![0; aw * ah];
552 downscale_gray(
553 &self.frames[self.current_slot],
554 self.width,
555 self.height,
556 self.factor as usize,
557 &mut data,
558 );
559 Arc::new(Plane {
560 width: aw,
561 height: ah,
562 data,
563 })
564 }
565 };
566 let features = match features {
567 Some(f) if !scan.duplicate => Arc::new(f.into_iter().map(stored).collect()),
568 _ => match (scan.duplicate, &self.previous_features) {
569 (true, Some(previous)) => previous.clone(),
570 _ => Arc::new(
571 extract_features(&gray.data, aw, ah, FALLBACK_FEATURES, None)
572 .into_iter()
573 .map(stored)
574 .collect(),
575 ),
576 },
577 };
578 self.native_filled = false;
579 self.stage = Stage::Frame(Box::new(FrameWork {
580 index,
581 time,
582 scan,
583 gray,
584 features,
585 placements: Vec::with_capacity(self.regions.len()),
586 region: 0,
587 step: None,
588 joined_ring: false,
589 }));
590 self.advance(labels, None)
591 }
592
593 pub fn provide_keyframes(
595 &mut self,
596 rows: Vec<Option<Keyframe>>,
597 labels: &[u8],
598 ) -> Result<Poll, SolveError> {
599 let waiting = match &self.stage {
600 Stage::Frame(work) => work.step.as_ref().is_some_and(|s| s.waiting()),
601 _ => false,
602 };
603 if !waiting {
604 return Err(SolveError::BadInput("no keyframes were asked for"));
605 }
606 self.advance(labels, Some(rows))
607 }
608
609 fn advance(
611 &mut self,
612 labels: &[u8],
613 mut rows: Option<Vec<Option<Keyframe>>>,
614 ) -> Result<Poll, SolveError> {
615 loop {
616 let Stage::Frame(work) = &mut self.stage else {
617 return Err(SolveError::BadInput("no frame in progress"));
618 };
619 if work.region == self.regions.len() {
620 return Ok(Poll::Done(self.finish_frame()));
621 }
622 let i = work.region;
623 let step = work.step.take();
624 let ctx = region::FrameCtx {
625 index: work.index,
626 time: work.time,
627 scan: work.scan,
628 gray: work.gray.clone(),
629 features: work.features.clone(),
630 };
631 let result = match step {
632 None => self.step_region(i, &ctx, labels),
633 Some(step) => self.resume_region(i, &ctx, *step, rows.take().unwrap_or_default()),
634 };
635 let Stage::Frame(work) = &mut self.stage else {
636 unreachable!()
637 };
638 match result {
639 Ok(region::Outcome::Placed(placement, observed)) => {
640 work.joined_ring |= observed;
641 work.placements.push(placement);
642 work.region += 1;
643 }
644 Ok(region::Outcome::Wait(step, ids)) => {
645 work.step = Some(step);
646 return Ok(Poll::NeedKeyframes(ids));
647 }
648 Err(e) => {
649 self.stage = Stage::Idle;
650 return Err(e);
651 }
652 }
653 }
654 }
655
656 fn finish_frame(&mut self) -> FrameDone {
658 let Stage::Frame(work) = std::mem::replace(&mut self.stage, Stage::Idle) else {
659 unreachable!()
660 };
661 let FrameWork {
662 index,
663 gray,
664 features,
665 placements,
666 joined_ring,
667 ..
668 } = *work;
669 let voting = if joined_ring {
670 let finalized = self.voting.push_frame(index);
671 self.records(finalized)
672 } else {
673 Vec::new()
674 };
675 self.previous_plan = Some(placements.clone());
676 self.previous_features = Some(features);
677 self.previous_slot = Some(self.current_slot);
678 self.previous_gray = Some(gray);
679 FrameDone {
680 placements,
681 joined_ring,
682 voting,
683 }
684 }
685
686 fn records(&self, finalized: Vec<Finalized>) -> Vec<VotingRecord> {
687 let moving: Vec<&SolveRegion> = self.regions.iter().filter(|r| r.moving).collect();
688 finalized
689 .into_iter()
690 .map(|f| VotingRecord {
691 index: f.index,
692 verdicts: f
693 .verdicts
694 .into_iter()
695 .map(|v| {
696 let b = self.voting.slots[v.slot].box_;
697 VotingVerdict {
698 region: moving[v.slot].id.clone(),
699 x0: b.x0,
700 y0: b.y0,
701 w: b.w,
702 h: b.h,
703 bits: v.bits,
704 clean: v.clean,
705 }
706 })
707 .collect(),
708 voted_layers: f.voted_layers,
709 thin_layers: f.thin_layers,
710 })
711 .collect()
712 }
713
714 pub fn drain_voting(&mut self) -> Vec<VotingRecord> {
716 let finalized = self.voting.drain();
717 self.records(finalized)
718 }
719
720 fn native(&mut self) -> &[u8] {
722 if !self.native_filled {
723 grayscale(&self.frames[self.current_slot], &mut self.native);
724 self.native_filled = true;
725 }
726 &self.native
727 }
728
729 fn canvas_slot(&mut self, canvas_id: &str) -> u32 {
730 let next = self.canvas_slots.len() as u32;
731 *self
732 .canvas_slots
733 .entry(canvas_id.to_string())
734 .or_insert(next)
735 }
736
737 fn new_canvas(&mut self, i: usize, time: f64) {
739 let region = &self.regions[i];
740 let state = &mut self.states[i];
741 state.canvas_id = format!("{}-part-{}", region.id, state.fragment);
742 let name = if state.fragment > 0 {
743 format!("{} · 未定位片段 {}", region.name, state.fragment)
744 } else {
745 region.name.clone()
746 };
747 let meta = CanvasMeta {
748 id: state.canvas_id.clone(),
749 layer: region.id.clone(),
750 name,
751 kind: region.kind.clone(),
752 fragment: state.fragment,
753 first_time: time,
754 last_time: time,
755 attached_to: None,
756 };
757 self.canvases.insert(meta.id.clone(), meta.clone());
758 self.effects.push(Effect::Canvas {
759 meta,
760 created: true,
761 });
762 }
763
764 fn diagnostic(&mut self, d: Diagnostic) {
765 self.effects.push(Effect::Diagnostic(d));
766 }
767
768 fn graph_writes(&mut self, writes: Vec<Write>) {
769 self.effects.extend(writes.into_iter().map(Effect::Graph));
770 }
771
772 fn retrieve(
774 &mut self,
775 i: usize,
776 frame: u32,
777 own: &[Feature],
778 exclude: Option<&str>,
779 ) -> Retrieval {
780 let descriptors: Vec<_> = own.iter().map(|f| f.descriptor).collect();
781 let retrieval =
782 self.index
783 .retrieve(&self.regions[i].id, frame, &descriptors, exclude, None);
784 if retrieval.budget_warning {
785 self.diagnostic(Diagnostic::new(
786 crate::engine::keyframes::BUDGET_DIAGNOSTIC,
787 Severity::Warning,
788 ));
789 }
790 retrieval
791 }
792}
793
794#[cfg(test)]
795mod tests;