Skip to content

Correctly compute keyframe at timestamp 0; Early return when possible#354

Merged
mrobinson merged 6 commits into
mainfrom
fix-quantization
May 18, 2026
Merged

Correctly compute keyframe at timestamp 0; Early return when possible#354
mrobinson merged 6 commits into
mainfrom
fix-quantization

Conversation

@yezhizhen
Copy link
Copy Markdown
Member

@yezhizhen yezhizhen commented Apr 20, 2026

Make early return happen earlier:

  1. total_progress == 1.0 and total_progress < 0 now handled immediately. Also make it direction aware.
  2. Zero-interval: we exit early to avoid division‑by‑zero in percentage_between_keyframes

Try
Servo PR: servo/servo#44365

@yezhizhen
Copy link
Copy Markdown
Member Author

@Loirooriol Just friendly reminder.

Comment thread style/servo/animation.rs Outdated

let prev_keyframe = &self.computed_steps[prev_keyframe_index];
let Some(next_keyframe_index) = next_keyframe_index else {
debug_assert!(false, "next_keyframe_index should always be Some");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using debug_unreachable may be clearer.
Also the message isn't providing much, it should rather explain why.

Copy link
Copy Markdown
Member Author

@yezhizhen yezhizhen May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 2701332

The explanation is a bit lengthy tho. We are relying on

debug_assert!(intermediate_steps.first().unwrap().start_percentage == 0.);
debug_assert!(intermediate_steps.last().unwrap().start_percentage == 1.);

Comment thread style/servo/animation.rs Outdated
// Progress clamped to the current iteration (0.0 to 1.0).
let total_progress = progress.min(self.current_iteration_end_progress()).max(0.0);

if total_progress >= 1.0 {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can this handle the equality, but above you check progress < 0.0?

Copy link
Copy Markdown
Member Author

@yezhizhen yezhizhen May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In 2701332 I updated to ==1.0 as it is always in [0,1].

but above you check progress < 0.0?

At progress == 0.0, we don't want to early return because we need to reach the step‑function handling block at L878 we added.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But don't we have the same jumping problem at the end?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

servo/servo#43086

End has always been fine. We only have problem at the beginning.

@yezhizhen yezhizhen marked this pull request as draft May 1, 2026 06:01
@yezhizhen yezhizhen force-pushed the fix-quantization branch from 398c241 to 1d1f9ce Compare May 1, 2026 06:32
@yezhizhen yezhizhen marked this pull request as ready for review May 1, 2026 06:55
@yezhizhen yezhizhen force-pushed the fix-quantization branch from dbc9ddd to 0cf455c Compare May 1, 2026 08:36
@yezhizhen yezhizhen requested a review from Loirooriol May 1, 2026 08:45
@yezhizhen yezhizhen force-pushed the fix-quantization branch from 0cf455c to ff53dff Compare May 6, 2026 08:57
@yezhizhen
Copy link
Copy Markdown
Member Author

@Loirooriol Do you still have other concerns for this?

Copy link
Copy Markdown
Contributor

@xiaochengh xiaochengh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My previous comments at #342 seem unaddressed:

@yezhizhen yezhizhen marked this pull request as draft May 14, 2026 06:55
@yezhizhen yezhizhen marked this pull request as ready for review May 14, 2026 07:07
@yezhizhen
Copy link
Copy Markdown
Member Author

yezhizhen commented May 14, 2026

I have removed special casing for step functions in 4c6b772. It is no longer imported.

This should be much more straightforward to review now. We do miss one early return, but the readability is worth it.

Now the PR is mostly renaming, reordering, adding comments, and computing index at boundary correctly.

@yezhizhen
Copy link
Copy Markdown
Member Author

@Loirooriol Xiaocheng would probably be off this week. Do you mind checking this again?

yezhizhen added 4 commits May 14, 2026 15:59
…rst keyframe to be selected at progress 0; changed to < for normal direction (kept <= for reverse to avoid #342 (comment)) and added direction‑aware step‑function handling.

- Guard against division‑by‑zero for zero‑length keyframe intervals.

1. total_progress ≥ 1.0 and total_progress < 0 now handled immediately. Also make it direction aware.
2. At timestamp 0, for normal direction, non‑jump step functions (jump‑end, jump‑none, end) now return the start‑keyframe value early, instead of proceeding to interpolation.
3. Zero-interval: we exit early to avoid division‑by‑zero in `percentage_between_keyframes`

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
Copy link
Copy Markdown
Contributor

@xiaochengh xiaochengh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation change looks very reasonable to me.

Comment thread style/servo/animation.rs
@yezhizhen
Copy link
Copy Markdown
Member Author

@Loirooriol @mrobinson I don't mean to nag. This isn't rocket science, but has been pended for two months. I also asked @Loirooriol two weeks ago if you still have concerns but just getting ignored.

@xiaochengh
Copy link
Copy Markdown
Contributor

Nit: maybe we need to modify the PR title, since the special handling for step functions have been removed?

@yezhizhen yezhizhen changed the title Fix CSS step timing functions jump-both, jump-start, start at timestamp 0; Early return when possible Correctly compute first keyframe at timestamp 0; Early return when possible May 18, 2026
@yezhizhen yezhizhen changed the title Correctly compute first keyframe at timestamp 0; Early return when possible Correctly compute keyframe at timestamp 0; Early return when possible May 18, 2026
Copy link
Copy Markdown
Member

@mrobinson mrobinson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good with the nits below:

Comment thread style/servo/animation.rs Outdated
Comment thread style/servo/animation.rs Outdated
Comment thread style/servo/animation.rs Outdated
Comment on lines +796 to +801
let keyframe = match self.current_direction {
AnimationDirection::Normal => self.computed_steps.first().unwrap(),
AnimationDirection::Reverse => self.computed_steps.last().unwrap(),
_ => unreachable!(),
};
add_declarations_to_map(keyframe);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's avoid unwrap() here and also below:

Suggested change
let keyframe = match self.current_direction {
AnimationDirection::Normal => self.computed_steps.first().unwrap(),
AnimationDirection::Reverse => self.computed_steps.last().unwrap(),
_ => unreachable!(),
};
add_declarations_to_map(keyframe);
if let Some(keyframe) = match self.current_direction {
AnimationDirection::Normal => self.computed_steps.first(),
AnimationDirection::Reverse => self.computed_steps.last(),
_ => unreachable!(),
} {
add_declarations_to_map(keyframe);
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// We should always have a first and a last step, even if these are just
// generated by KeyframesStepValue::ComputedValues.
debug_assert!(intermediate_steps.first().unwrap().start_percentage == 0.);
debug_assert!(intermediate_steps.last().unwrap().start_percentage == 1.);

But the animation is guaranteed to have them. We also unwrapped earlier in previous code.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still we should avoid unwrap when possible (servo/servo#40744). If the code above changes, the person changing it might not get to this code, which can cause a panic later on.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! I guess there's nothing we can do with unwrap in this debug_assert.
Done in 01ef6d6

Comment thread style/servo/animation.rs Outdated
@yezhizhen
Copy link
Copy Markdown
Member Author

@mrobinson Thanks for review! Addressing this soon.

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
Copy link
Copy Markdown
Member Author

@yezhizhen yezhizhen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have replaced the unreachable string as "Current animation direction can only be normal or reverse" instead, to be consistent with

/// The current animation direction. This can only be `normal` or `reverse`.
pub current_direction: AnimationDirection,
. Also added it to fn iterate.

Comment thread style/servo/animation.rs Outdated
Comment on lines +796 to +801
let keyframe = match self.current_direction {
AnimationDirection::Normal => self.computed_steps.first().unwrap(),
AnimationDirection::Reverse => self.computed_steps.last().unwrap(),
_ => unreachable!(),
};
add_declarations_to_map(keyframe);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// We should always have a first and a last step, even if these are just
// generated by KeyframesStepValue::ComputedValues.
debug_assert!(intermediate_steps.first().unwrap().start_percentage == 0.);
debug_assert!(intermediate_steps.last().unwrap().start_percentage == 1.);

But the animation is guaranteed to have them. We also unwrapped earlier in previous code.

@yezhizhen yezhizhen requested a review from mrobinson May 18, 2026 09:59
Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
@mrobinson mrobinson added this pull request to the merge queue May 18, 2026
Merged via the queue into main with commit f611007 May 18, 2026
5 checks passed
@mrobinson mrobinson deleted the fix-quantization branch May 18, 2026 13:39
pull Bot pushed a commit to Patreos998/servo that referenced this pull request May 18, 2026
…ame correctly; Eliminate Stylo static preference hashing (servo#44365)

Stylo PR: servo/stylo#354

This also includes servo/stylo#367 which
eliminates static pref hashing

Testing: New passes in existing tests: revert-rule, css-easing,
css-logical.

[revert-rule](https://drafts.csswg.org/css-cascade-5/#revert-rule-keyword)
seems to be one of latest CSS keywords. I cannot even find it in MDN.

Fixes: servo#43086

I'm unable to reopen the previous PR servo#43594.

---------

Signed-off-by: Euclid Ye <yezhizhenjiakang@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants