Bridge interrupted phased retargets
This commit is contained in:
parent
e7f9a5cb09
commit
09305ab026
1 changed files with 213 additions and 3 deletions
216
src/state.rs
216
src/state.rs
|
|
@ -7,7 +7,8 @@ use {
|
||||||
RetainedToplevel,
|
RetainedToplevel,
|
||||||
expand_damage_rect,
|
expand_damage_rect,
|
||||||
multiphase::{
|
multiphase::{
|
||||||
MultiphaseRequest, MultiphaseWindow, MultiphaseWindowHierarchy,
|
MultiphasePhase, MultiphasePlan, MultiphasePlanFailure, MultiphaseRequest,
|
||||||
|
MultiphaseWindow, MultiphaseWindowHierarchy,
|
||||||
partition_motion_groups, plan_no_overlap_with_diagnostics, validate_phase_paths,
|
partition_motion_groups, plan_no_overlap_with_diagnostics, validate_phase_paths,
|
||||||
},
|
},
|
||||||
spawn_in_start_rect,
|
spawn_in_start_rect,
|
||||||
|
|
@ -205,6 +206,56 @@ fn layout_animation_group_uses_plain(
|
||||||
.any(|&idx| candidates[idx].style == AnimationStyle::Plain)
|
.any(|&idx| candidates[idx].style == AnimationStyle::Plain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bridged_retarget_plan(
|
||||||
|
request: &MultiphaseRequest,
|
||||||
|
candidates: &[LayoutAnimationCandidate],
|
||||||
|
group: &[usize],
|
||||||
|
bridge_paths: &[Vec<(Rect, Rect)>],
|
||||||
|
bridge_phase_count: usize,
|
||||||
|
follow_phases: &[MultiphasePhase],
|
||||||
|
) -> Result<MultiphasePlan, MultiphasePlanFailure> {
|
||||||
|
let mut paths = vec![];
|
||||||
|
for (group_pos, &idx) in group.iter().enumerate() {
|
||||||
|
let candidate = &candidates[idx];
|
||||||
|
let window = request.windows[group_pos];
|
||||||
|
let Some(bridge_path) = bridge_paths.get(group_pos) else {
|
||||||
|
return Err(MultiphasePlanFailure::NoPattern);
|
||||||
|
};
|
||||||
|
let mut path = bridge_path.clone();
|
||||||
|
let mut current = path
|
||||||
|
.last()
|
||||||
|
.map(|(_, to)| *to)
|
||||||
|
.unwrap_or(window.from);
|
||||||
|
while path.len() < bridge_phase_count {
|
||||||
|
path.push((current, current));
|
||||||
|
}
|
||||||
|
if current != candidate.old {
|
||||||
|
return Err(MultiphasePlanFailure::NoPattern);
|
||||||
|
}
|
||||||
|
for phase in follow_phases {
|
||||||
|
match phase
|
||||||
|
.steps
|
||||||
|
.iter()
|
||||||
|
.find(|step| step.node_id == candidate.node_id)
|
||||||
|
{
|
||||||
|
Some(step) => {
|
||||||
|
if step.from != current {
|
||||||
|
return Err(MultiphasePlanFailure::NoPattern);
|
||||||
|
}
|
||||||
|
path.push((step.from, step.to));
|
||||||
|
current = step.to;
|
||||||
|
}
|
||||||
|
None => path.push((current, current)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if current != window.to {
|
||||||
|
return Err(MultiphasePlanFailure::NoPattern);
|
||||||
|
}
|
||||||
|
paths.push(path);
|
||||||
|
}
|
||||||
|
validate_phase_paths(request, &paths)
|
||||||
|
}
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub pid: c::pid_t,
|
pub pid: c::pid_t,
|
||||||
pub kb_ctx: KbvmContext,
|
pub kb_ctx: KbvmContext,
|
||||||
|
|
@ -1721,6 +1772,9 @@ impl State {
|
||||||
if self.start_existing_phased_retarget(candidates, windows, group, &request, now_nsec) {
|
if self.start_existing_phased_retarget(candidates, windows, group, &request, now_nsec) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if self.start_bridged_phased_retarget(candidates, windows, group, &request, now_nsec) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
let plan = match plan_no_overlap_with_diagnostics(&request) {
|
let plan = match plan_no_overlap_with_diagnostics(&request) {
|
||||||
Ok(plan) => plan,
|
Ok(plan) => plan,
|
||||||
Err(diagnostic) => {
|
Err(diagnostic) => {
|
||||||
|
|
@ -1770,6 +1824,97 @@ impl State {
|
||||||
self.start_multiphase_plan(candidates, windows, group, &plan.phases, now_nsec)
|
self.start_multiphase_plan(candidates, windows, group, &plan.phases, now_nsec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn start_bridged_phased_retarget(
|
||||||
|
self: &Rc<Self>,
|
||||||
|
candidates: &[LayoutAnimationCandidate],
|
||||||
|
windows: &[MultiphaseWindow],
|
||||||
|
group: &[usize],
|
||||||
|
request: &MultiphaseRequest,
|
||||||
|
now_nsec: u64,
|
||||||
|
) -> bool {
|
||||||
|
let mut bridge_paths = vec![];
|
||||||
|
let mut bridge_phase_count = 0;
|
||||||
|
let mut has_bridge = false;
|
||||||
|
for &idx in group {
|
||||||
|
let candidate = &candidates[idx];
|
||||||
|
let window = windows[idx];
|
||||||
|
if window.from == candidate.old {
|
||||||
|
bridge_paths.push(vec![]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let Some(path) =
|
||||||
|
self.animations
|
||||||
|
.phased_route_to(candidate.node_id, candidate.old, now_nsec)
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if !path.is_empty() {
|
||||||
|
has_bridge = true;
|
||||||
|
bridge_phase_count = bridge_phase_count.max(path.len());
|
||||||
|
}
|
||||||
|
bridge_paths.push(path);
|
||||||
|
}
|
||||||
|
if !has_bridge {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let settled_windows: Vec<_> = group
|
||||||
|
.iter()
|
||||||
|
.map(|&idx| {
|
||||||
|
let candidate = &candidates[idx];
|
||||||
|
MultiphaseWindow::with_hierarchy(
|
||||||
|
candidate.node_id,
|
||||||
|
candidate.old,
|
||||||
|
candidate.new,
|
||||||
|
candidate.hierarchy,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let Some(first) = settled_windows.first() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let mut bounds = first.from.union(first.to);
|
||||||
|
for window in &settled_windows[1..] {
|
||||||
|
bounds = bounds.union(window.from).union(window.to);
|
||||||
|
}
|
||||||
|
let settled_request = MultiphaseRequest {
|
||||||
|
bounds,
|
||||||
|
windows: settled_windows,
|
||||||
|
clearance: self.layout_animation_clearance(),
|
||||||
|
};
|
||||||
|
let follow_plan = match plan_no_overlap_with_diagnostics(&settled_request) {
|
||||||
|
Ok(plan) => plan,
|
||||||
|
Err(diagnostic) => {
|
||||||
|
log::debug!(
|
||||||
|
"bridged phased retarget follow-up rejected for group {:?}: {:?}",
|
||||||
|
group,
|
||||||
|
diagnostic
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let plan = match bridged_retarget_plan(
|
||||||
|
request,
|
||||||
|
candidates,
|
||||||
|
group,
|
||||||
|
&bridge_paths,
|
||||||
|
bridge_phase_count,
|
||||||
|
&follow_plan.phases,
|
||||||
|
) {
|
||||||
|
Ok(plan) => plan,
|
||||||
|
Err(error) => {
|
||||||
|
log::debug!(
|
||||||
|
"bridged phased retarget rejected for group {:?}: {:?}",
|
||||||
|
group,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
log::debug!("bridging active phased animation for group {:?}", group);
|
||||||
|
self.start_multiphase_plan(candidates, windows, group, &plan.phases, now_nsec)
|
||||||
|
}
|
||||||
|
|
||||||
fn start_multiphase_plan(
|
fn start_multiphase_plan(
|
||||||
self: &Rc<Self>,
|
self: &Rc<Self>,
|
||||||
candidates: &[LayoutAnimationCandidate],
|
candidates: &[LayoutAnimationCandidate],
|
||||||
|
|
@ -2512,10 +2657,24 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn candidate(node_id: u32, style: AnimationStyle) -> LayoutAnimationCandidate {
|
fn candidate(node_id: u32, style: AnimationStyle) -> LayoutAnimationCandidate {
|
||||||
|
candidate_rects(
|
||||||
|
node_id,
|
||||||
|
rect(0, 0, 100, 100),
|
||||||
|
rect(100, 0, 200, 100),
|
||||||
|
style,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn candidate_rects(
|
||||||
|
node_id: u32,
|
||||||
|
old: Rect,
|
||||||
|
new: Rect,
|
||||||
|
style: AnimationStyle,
|
||||||
|
) -> LayoutAnimationCandidate {
|
||||||
LayoutAnimationCandidate {
|
LayoutAnimationCandidate {
|
||||||
node_id: NodeId(node_id),
|
node_id: NodeId(node_id),
|
||||||
old: rect(0, 0, 100, 100),
|
old,
|
||||||
new: rect(100, 0, 200, 100),
|
new,
|
||||||
curve: AnimationCurve::Linear,
|
curve: AnimationCurve::Linear,
|
||||||
style,
|
style,
|
||||||
hierarchy: MultiphaseWindowHierarchy::default(),
|
hierarchy: MultiphaseWindowHierarchy::default(),
|
||||||
|
|
@ -2533,6 +2692,57 @@ mod tests {
|
||||||
assert!(layout_animation_group_uses_plain(&candidates, &[0, 1]));
|
assert!(layout_animation_group_uses_plain(&candidates, &[0, 1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bridged_retarget_handles_second_rotation_interrupt() {
|
||||||
|
let a_left = rect(0, 0, 100, 100);
|
||||||
|
let c_mid = rect(100, 0, 200, 100);
|
||||||
|
let c_left = a_left;
|
||||||
|
let a_mid = c_mid;
|
||||||
|
let c_current = rect(150, 50, 250, 100);
|
||||||
|
let c_mid_lane = rect(100, 50, 200, 100);
|
||||||
|
let candidates = vec![
|
||||||
|
candidate_rects(1, a_left, a_mid, AnimationStyle::Multiphase),
|
||||||
|
candidate_rects(3, c_mid, c_left, AnimationStyle::Multiphase),
|
||||||
|
];
|
||||||
|
let request = MultiphaseRequest {
|
||||||
|
bounds: rect(0, 0, 250, 100),
|
||||||
|
windows: vec![
|
||||||
|
MultiphaseWindow::new(NodeId(1), a_left, a_mid),
|
||||||
|
MultiphaseWindow::new(NodeId(3), c_current, c_left),
|
||||||
|
],
|
||||||
|
clearance: 0,
|
||||||
|
};
|
||||||
|
let settled_request = MultiphaseRequest {
|
||||||
|
bounds: rect(0, 0, 200, 100),
|
||||||
|
windows: vec![
|
||||||
|
MultiphaseWindow::new(NodeId(1), a_left, a_mid),
|
||||||
|
MultiphaseWindow::new(NodeId(3), c_mid, c_left),
|
||||||
|
],
|
||||||
|
clearance: 0,
|
||||||
|
};
|
||||||
|
let follow_plan = plan_no_overlap_with_diagnostics(&settled_request).unwrap();
|
||||||
|
let bridge_paths = vec![vec![], vec![(c_current, c_mid_lane), (c_mid_lane, c_mid)]];
|
||||||
|
|
||||||
|
let plan = bridged_retarget_plan(
|
||||||
|
&request,
|
||||||
|
&candidates,
|
||||||
|
&[0, 1],
|
||||||
|
&bridge_paths,
|
||||||
|
2,
|
||||||
|
&follow_plan.phases,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(plan
|
||||||
|
.phases
|
||||||
|
.iter()
|
||||||
|
.any(|phase| phase.steps.iter().any(|step| step.node_id == NodeId(1))));
|
||||||
|
assert!(plan
|
||||||
|
.phases
|
||||||
|
.iter()
|
||||||
|
.any(|phase| phase.steps.iter().any(|step| step.node_id == NodeId(3))));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn layout_animation_candidates_coalesce_duplicate_nodes() {
|
fn layout_animation_candidates_coalesce_duplicate_nodes() {
|
||||||
let source = MultiphaseHierarchyPosition {
|
let source = MultiphaseHierarchyPosition {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue