Eliminate redundant clamping from sample-interpolated curves (#15620)

# Objective

Currently, sample-interpolated curves (such as those used by the glTF
loader for animations) do unnecessary extra work when `sample_clamped`
is called, since their implementations of `sample_unchecked` are already
clamped. Eliminating this redundant sampling is a small, easy
performance win which doesn't compromise on the animation system's
internal usage of `sample_clamped`, which guarantees that it never
samples curves out-of-bounds.

## Solution

For sample-interpolated curves, define `sample_clamped` in the way
`sample_unchecked` is currently defined, and then redirect
`sample_unchecked` to `sample_clamped`. This is arguably a more
idiomatic way of using the `cores` as well, which is nice.

## Testing

Ran `many_foxes` to make sure I didn't break anything.
This commit is contained in:
Matty 2024-10-03 14:26:41 -04:00 committed by GitHub
parent 46180a75f8
commit 528ca4f95e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 77 additions and 15 deletions

View File

@ -1021,14 +1021,14 @@ where
} }
#[inline] #[inline]
fn sample_unchecked(&self, t: f32) -> T { fn sample_clamped(&self, t: f32) -> T {
// `UnevenCore::sample_with` is implicitly clamped.
self.core.sample_with(t, <T as Animatable>::interpolate) self.core.sample_with(t, <T as Animatable>::interpolate)
} }
#[inline] #[inline]
fn sample_clamped(&self, t: f32) -> T { fn sample_unchecked(&self, t: f32) -> T {
// Sampling by keyframes is automatically clamped to the keyframe bounds. self.sample_clamped(t)
self.sample_unchecked(t)
} }
} }

View File

@ -23,10 +23,15 @@ where
} }
#[inline] #[inline]
fn sample_unchecked(&self, t: f32) -> T { fn sample_clamped(&self, t: f32) -> T {
self.core self.core
.sample_with(t, |x, y, t| if t >= 1.0 { y.clone() } else { x.clone() }) .sample_with(t, |x, y, t| if t >= 1.0 { y.clone() } else { x.clone() })
} }
#[inline]
fn sample_unchecked(&self, t: f32) -> T {
self.sample_clamped(t)
}
} }
impl<T> SteppedKeyframeCurve<T> { impl<T> SteppedKeyframeCurve<T> {
@ -57,7 +62,7 @@ where
} }
#[inline] #[inline]
fn sample_unchecked(&self, t: f32) -> V { fn sample_clamped(&self, t: f32) -> V {
match self.core.sample_interp_timed(t) { match self.core.sample_interp_timed(t) {
// In all the cases where only one frame matters, defer to the position within it. // In all the cases where only one frame matters, defer to the position within it.
InterpolationDatum::Exact((_, v)) InterpolationDatum::Exact((_, v))
@ -69,6 +74,11 @@ where
} }
} }
} }
#[inline]
fn sample_unchecked(&self, t: f32) -> V {
self.sample_clamped(t)
}
} }
impl<T> CubicKeyframeCurve<T> { impl<T> CubicKeyframeCurve<T> {
@ -112,7 +122,7 @@ impl Curve<Quat> for CubicRotationCurve {
} }
#[inline] #[inline]
fn sample_unchecked(&self, t: f32) -> Quat { fn sample_clamped(&self, t: f32) -> Quat {
let vec = match self.core.sample_interp_timed(t) { let vec = match self.core.sample_interp_timed(t) {
// In all the cases where only one frame matters, defer to the position within it. // In all the cases where only one frame matters, defer to the position within it.
InterpolationDatum::Exact((_, v)) InterpolationDatum::Exact((_, v))
@ -125,6 +135,11 @@ impl Curve<Quat> for CubicRotationCurve {
}; };
Quat::from_vec4(vec.normalize()) Quat::from_vec4(vec.normalize())
} }
#[inline]
fn sample_unchecked(&self, t: f32) -> Quat {
self.sample_clamped(t)
}
} }
impl CubicRotationCurve { impl CubicRotationCurve {
@ -170,7 +185,7 @@ where
} }
#[inline] #[inline]
fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> { fn sample_iter_clamped(&self, t: f32) -> impl Iterator<Item = T> {
match self.core.sample_interp(t) { match self.core.sample_interp(t) {
InterpolationDatum::Exact(v) InterpolationDatum::Exact(v)
| InterpolationDatum::LeftTail(v) | InterpolationDatum::LeftTail(v)
@ -182,6 +197,11 @@ where
} }
} }
} }
#[inline]
fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> {
self.sample_iter_clamped(t)
}
} }
impl<T> WideLinearKeyframeCurve<T> { impl<T> WideLinearKeyframeCurve<T> {
@ -219,7 +239,7 @@ where
} }
#[inline] #[inline]
fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> { fn sample_iter_clamped(&self, t: f32) -> impl Iterator<Item = T> {
match self.core.sample_interp(t) { match self.core.sample_interp(t) {
InterpolationDatum::Exact(v) InterpolationDatum::Exact(v)
| InterpolationDatum::LeftTail(v) | InterpolationDatum::LeftTail(v)
@ -234,6 +254,11 @@ where
} }
} }
} }
#[inline]
fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> {
self.sample_iter_clamped(t)
}
} }
impl<T> WideSteppedKeyframeCurve<T> { impl<T> WideSteppedKeyframeCurve<T> {
@ -269,7 +294,7 @@ where
self.core.domain() self.core.domain()
} }
fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> { fn sample_iter_clamped(&self, t: f32) -> impl Iterator<Item = T> {
match self.core.sample_interp_timed(t) { match self.core.sample_interp_timed(t) {
InterpolationDatum::Exact((_, v)) InterpolationDatum::Exact((_, v))
| InterpolationDatum::LeftTail((_, v)) | InterpolationDatum::LeftTail((_, v))
@ -285,6 +310,11 @@ where
), ),
} }
} }
#[inline]
fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> {
self.sample_iter_clamped(t)
}
} }
/// An error indicating that a multisampling keyframe curve could not be constructed. /// An error indicating that a multisampling keyframe curve could not be constructed.

View File

@ -54,13 +54,21 @@ impl<T> Curve<T> for ColorCurve<T>
where where
T: Mix + Clone, T: Mix + Clone,
{ {
#[inline]
fn domain(&self) -> Interval { fn domain(&self) -> Interval {
self.core.domain() self.core.domain()
} }
fn sample_unchecked(&self, t: f32) -> T { #[inline]
fn sample_clamped(&self, t: f32) -> T {
// `EvenCore::sample_with` clamps the input implicitly.
self.core.sample_with(t, T::mix) self.core.sample_with(t, T::mix)
} }
#[inline]
fn sample_unchecked(&self, t: f32) -> T {
self.sample_clamped(t)
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -94,9 +94,15 @@ where
} }
#[inline] #[inline]
fn sample_unchecked(&self, t: f32) -> T { fn sample_clamped(&self, t: f32) -> T {
// `EvenCore::sample_with` is implicitly clamped.
self.core.sample_with(t, &self.interpolation) self.core.sample_with(t, &self.interpolation)
} }
#[inline]
fn sample_unchecked(&self, t: f32) -> T {
self.sample_clamped(t)
}
} }
impl<T, I> SampleCurve<T, I> { impl<T, I> SampleCurve<T, I> {
@ -143,10 +149,16 @@ where
} }
#[inline] #[inline]
fn sample_unchecked(&self, t: f32) -> T { fn sample_clamped(&self, t: f32) -> T {
// `EvenCore::sample_with` is implicitly clamped.
self.core self.core
.sample_with(t, <T as StableInterpolate>::interpolate_stable) .sample_with(t, <T as StableInterpolate>::interpolate_stable)
} }
#[inline]
fn sample_unchecked(&self, t: f32) -> T {
self.sample_clamped(t)
}
} }
impl<T> SampleAutoCurve<T> { impl<T> SampleAutoCurve<T> {
@ -242,9 +254,15 @@ where
} }
#[inline] #[inline]
fn sample_unchecked(&self, t: f32) -> T { fn sample_clamped(&self, t: f32) -> T {
// `UnevenCore::sample_with` is implicitly clamped.
self.core.sample_with(t, &self.interpolation) self.core.sample_with(t, &self.interpolation)
} }
#[inline]
fn sample_unchecked(&self, t: f32) -> T {
self.sample_clamped(t)
}
} }
impl<T, I> UnevenSampleCurve<T, I> { impl<T, I> UnevenSampleCurve<T, I> {
@ -301,10 +319,16 @@ where
} }
#[inline] #[inline]
fn sample_unchecked(&self, t: f32) -> T { fn sample_clamped(&self, t: f32) -> T {
// `UnevenCore::sample_with` is implicitly clamped.
self.core self.core
.sample_with(t, <T as StableInterpolate>::interpolate_stable) .sample_with(t, <T as StableInterpolate>::interpolate_stable)
} }
#[inline]
fn sample_unchecked(&self, t: f32) -> T {
self.sample_clamped(t)
}
} }
impl<T> UnevenSampleAutoCurve<T> { impl<T> UnevenSampleAutoCurve<T> {