servo: Merge #19042 - stylo: Switch to Servo style backend for compositor animations (from BorisChiou:stylo/animation/compositor); r=<try>

These are inter-dependent patches for bug 1340005. We add two FFIs to create the
AnimationValues of Opacity and Transform because we support these two properties
on the compositor. Besides, we factor out some common function in glue.rs, so we
can use the same code path for both main-thread and compositor-thread animations.

---
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix [Bug 1340005](https://bugzilla.mozilla.org/show_bug.cgi?id=1340005).
- [X] These changes do not require tests because we have automatic test coverage on Gecko already, and Servo doesn't support compositor animation for now.

Source-Repo: https://github.com/servo/servo
Source-Revision: 4e2c0e3277dda5d8bf462602268bbc1ec8ea26f7

--HG--
extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear
extra : subtree_revision : 66c1dd18d57495d78729a669ec255c087b42c5e1
This commit is contained in:
Boris Chiou 2017-10-27 14:38:43 -05:00
Родитель 5f973ae9ca
Коммит 1518c25a77
4 изменённых файлов: 275 добавлений и 130 удалений

Просмотреть файл

@ -2618,12 +2618,20 @@ extern "C" {
RawServoAnimationValueBorrowed)
-> f32;
}
extern "C" {
pub fn Servo_AnimationValue_Opacity(arg1: f32)
-> RawServoAnimationValueStrong;
}
extern "C" {
pub fn Servo_AnimationValue_GetTransform(value:
RawServoAnimationValueBorrowed,
list:
*mut RefPtr<nsCSSValueSharedList>);
}
extern "C" {
pub fn Servo_AnimationValue_Transform(list: *const nsCSSValueSharedList)
-> RawServoAnimationValueStrong;
}
extern "C" {
pub fn Servo_AnimationValue_DeepEqual(arg1:
RawServoAnimationValueBorrowed,
@ -2768,9 +2776,22 @@ extern "C" {
RawGeckoAnimationPropertySegmentBorrowed,
computed_timing:
RawGeckoComputedTimingBorrowed,
iteration_composite:
iter_composite:
IterationCompositeOperation);
}
extern "C" {
pub fn Servo_ComposeAnimationSegment(animation_segment:
RawGeckoAnimationPropertySegmentBorrowed,
underlying_value:
RawServoAnimationValueBorrowedOrNull,
last_value:
RawServoAnimationValueBorrowedOrNull,
iter_composite:
IterationCompositeOperation,
progress: f64,
current_iteration: u64)
-> RawServoAnimationValueStrong;
}
extern "C" {
pub fn Servo_DeclarationBlock_PropertyIsSet(declarations:
RawServoDeclarationBlockBorrowed,

Просмотреть файл

@ -23,6 +23,12 @@ impl nsCSSValue {
unsafe { mem::zeroed() }
}
/// Returns true if this nsCSSValue is none.
#[inline]
pub fn is_none(&self) -> bool {
self.mUnit == nsCSSUnit::eCSSUnit_None
}
/// Returns this nsCSSValue value as an integer, unchecked in release
/// builds.
pub fn integer_unchecked(&self) -> i32 {

Просмотреть файл

@ -3081,6 +3081,13 @@ fn static_assert() {
};
unsafe {
use gecko_bindings::structs::nsCSSKeyword;
use values::computed::Angle;
let get_array_angle = || -> Angle {
bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 1).get_angle()
};
match transform_function {
${computed_operation_arm("Matrix", "matrix3d", ["number"] * 16)}
${computed_operation_arm("Skew", "skew", ["angle"] * 2)}
@ -3092,23 +3099,59 @@ fn static_assert() {
["list"] * 2 + ["percentage"])}
${computed_operation_arm("AccumulateMatrix", "accumulatematrix",
["list"] * 2 + ["percentage_to_integer"])}
_ => panic!("We shouldn't set any other transform function types"),
// FIXME: Bug 1391145 will introduce new types for these keywords. For now, we
// temporarily don't use |computed_operation_arm| because these are special cases
// for compositor animations when we use Gecko style backend on the main thread,
// and I don't want to add too many special cases in |computed_operation_arm|.
//
// Note: Gecko only converts translate and scale into the corresponding primitive
// functions, so we still need to handle the following functions.
nsCSSKeyword::eCSSKeyword_skewx => {
ComputedOperation::Skew(get_array_angle(), Angle::zero())
},
nsCSSKeyword::eCSSKeyword_skewy => {
ComputedOperation::Skew(Angle::zero(), get_array_angle())
},
nsCSSKeyword::eCSSKeyword_rotatex => {
ComputedOperation::Rotate(1.0, 0.0, 0.0, get_array_angle())
},
nsCSSKeyword::eCSSKeyword_rotatey => {
ComputedOperation::Rotate(0.0, 1.0, 0.0, get_array_angle())
},
nsCSSKeyword::eCSSKeyword_rotatez | nsCSSKeyword::eCSSKeyword_rotate => {
ComputedOperation::Rotate(0.0, 0.0, 1.0, get_array_angle())
},
_ => panic!("{:?} is not an acceptable transform function", transform_function),
}
}
}
pub fn clone_transform(&self) -> longhands::transform::computed_value::T {
use properties::longhands::transform::computed_value;
if self.gecko.mSpecifiedTransform.mRawPtr.is_null() {
return computed_value::T(None);
return longhands::transform::computed_value::T(None);
}
let list = unsafe { (*self.gecko.mSpecifiedTransform.to_safe().get()).mHead.as_ref() };
let result = list.map(|list| {
list.into_iter()
.map(|value| Self::clone_single_transform_function(value))
.collect()
});
computed_value::T(result)
Self::clone_transform_from_list(list)
}
pub fn clone_transform_from_list(list: Option< &structs::root::nsCSSValueList>)
-> longhands::transform::computed_value::T {
let result = match list {
Some(list) => {
let vec: Vec<_> = list
.into_iter()
.filter_map(|value| {
// Handle none transform.
if value.is_none() {
None
} else {
Some(Self::clone_single_transform_function(value))
}
})
.collect();
if !vec.is_empty() { Some(vec) } else { None }
},
_ => None,
};
longhands::transform::computed_value::T(result)
}
${impl_transition_time_value('delay', 'Delay')}

Просмотреть файл

@ -67,6 +67,7 @@ use style::gecko_bindings::bindings::RawGeckoServoAnimationValueListBorrowed;
use style::gecko_bindings::bindings::RawGeckoServoAnimationValueListBorrowedMut;
use style::gecko_bindings::bindings::RawGeckoServoStyleRuleListBorrowedMut;
use style::gecko_bindings::bindings::RawServoAnimationValueBorrowed;
use style::gecko_bindings::bindings::RawServoAnimationValueBorrowedOrNull;
use style::gecko_bindings::bindings::RawServoAnimationValueMapBorrowedMut;
use style::gecko_bindings::bindings::RawServoAnimationValueStrong;
use style::gecko_bindings::bindings::RawServoAnimationValueTableBorrowed;
@ -411,6 +412,157 @@ pub extern "C" fn Servo_AnimationValues_ComputeDistance(from: RawServoAnimationV
from_value.compute_squared_distance(to_value).map(|d| d.sqrt()).unwrap_or(-1.0)
}
/// Compute one of the endpoints for the interpolation interval, compositing it with the
/// underlying value if needed.
/// An None returned value means, "Just use endpoint_value as-is."
/// It is the responsibility of the caller to ensure that |underlying_value| is provided
/// when it will be used.
fn composite_endpoint(
endpoint_value: Option<&RawOffsetArc<AnimationValue>>,
composite: CompositeOperation,
underlying_value: Option<&AnimationValue>,
) -> Option<AnimationValue> {
match endpoint_value {
Some(endpoint_value) => {
match composite {
CompositeOperation::Add => {
underlying_value
.expect("We should have an underlying_value")
.animate(endpoint_value, Procedure::Add).ok()
},
CompositeOperation::Accumulate => {
underlying_value
.expect("We should have an underlying value")
.animate(endpoint_value, Procedure::Accumulate { count: 1 })
.ok()
},
_ => None,
}
},
None => underlying_value.map(|v| v.clone()),
}
}
/// Accumulate one of the endpoints of the animation interval.
/// A returned value of None means, "Just use endpoint_value as-is."
fn accumulate_endpoint(
endpoint_value: Option<&RawOffsetArc<AnimationValue>>,
composited_value: Option<AnimationValue>,
last_value: &AnimationValue,
current_iteration: u64
) -> Option<AnimationValue> {
debug_assert!(endpoint_value.is_some() || composited_value.is_some(),
"Should have a suitable value to use");
let count = current_iteration;
match composited_value {
Some(endpoint) => {
last_value
.animate(&endpoint, Procedure::Accumulate { count })
.ok()
.or(Some(endpoint))
},
None => {
last_value
.animate(endpoint_value.unwrap(), Procedure::Accumulate { count })
.ok()
},
}
}
/// Compose the animation segment. We composite it with the underlying_value and last_value if
/// needed.
/// The caller is responsible for providing an underlying value and last value
/// in all situations where there are needed.
fn compose_animation_segment(
segment: RawGeckoAnimationPropertySegmentBorrowed,
underlying_value: Option<&AnimationValue>,
last_value: Option<&AnimationValue>,
iteration_composite: IterationCompositeOperation,
current_iteration: u64,
total_progress: f64,
segment_progress: f64,
) -> AnimationValue {
// Extract keyframe values.
let raw_from_value;
let keyframe_from_value = if !segment.mFromValue.mServo.mRawPtr.is_null() {
raw_from_value = unsafe { &*segment.mFromValue.mServo.mRawPtr };
Some(AnimationValue::as_arc(&raw_from_value))
} else {
None
};
let raw_to_value;
let keyframe_to_value = if !segment.mToValue.mServo.mRawPtr.is_null() {
raw_to_value = unsafe { &*segment.mToValue.mServo.mRawPtr };
Some(AnimationValue::as_arc(&raw_to_value))
} else {
None
};
let mut composited_from_value = composite_endpoint(keyframe_from_value,
segment.mFromComposite,
underlying_value);
let mut composited_to_value = composite_endpoint(keyframe_to_value,
segment.mToComposite,
underlying_value);
debug_assert!(keyframe_from_value.is_some() || composited_from_value.is_some(),
"Should have a suitable from value to use");
debug_assert!(keyframe_to_value.is_some() || composited_to_value.is_some(),
"Should have a suitable to value to use");
// Apply iteration composite behavior.
if iteration_composite == IterationCompositeOperation::Accumulate && current_iteration > 0 {
let last_value = last_value.unwrap_or_else(|| {
underlying_value.expect("Should have a valid underlying value")
});
composited_from_value = accumulate_endpoint(keyframe_from_value,
composited_from_value,
last_value,
current_iteration);
composited_to_value = accumulate_endpoint(keyframe_to_value,
composited_to_value,
last_value,
current_iteration);
}
// Use the composited value if there is one, otherwise, use the original keyframe value.
let from = composited_from_value.as_ref().unwrap_or_else(|| keyframe_from_value.unwrap());
let to = composited_to_value.as_ref().unwrap_or_else(|| keyframe_to_value.unwrap());
if segment.mToKey == segment.mFromKey {
return if total_progress < 0. { from.clone() } else { to.clone() };
}
match from.animate(to, Procedure::Interpolate { progress: segment_progress }) {
Ok(value) => value,
_ => if segment_progress < 0.5 { from.clone() } else { to.clone() },
}
}
#[no_mangle]
pub extern "C" fn Servo_ComposeAnimationSegment(
segment: RawGeckoAnimationPropertySegmentBorrowed,
underlying_value: RawServoAnimationValueBorrowedOrNull,
last_value: RawServoAnimationValueBorrowedOrNull,
iteration_composite: IterationCompositeOperation,
progress: f64,
current_iteration: u64
) -> RawServoAnimationValueStrong {
let underlying_value = AnimationValue::arc_from_borrowed(&underlying_value).map(|v| &**v);
let last_value = AnimationValue::arc_from_borrowed(&last_value).map(|v| &**v);
let result = compose_animation_segment(segment,
underlying_value,
last_value,
iteration_composite,
current_iteration,
progress,
progress);
Arc::new(result).into_strong()
}
#[no_mangle]
pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMapBorrowedMut,
base_values: RawServoAnimationValueTableBorrowed,
@ -461,125 +613,31 @@ pub extern "C" fn Servo_AnimationCompose(raw_value_map: RawServoAnimationValueMa
return;
}
// Extract keyframe values.
let raw_from_value;
let keyframe_from_value = if !segment.mFromValue.mServo.mRawPtr.is_null() {
raw_from_value = unsafe { &*segment.mFromValue.mServo.mRawPtr };
Some(AnimationValue::as_arc(&raw_from_value))
let raw_last_value;
let last_value = if !last_segment.mToValue.mServo.mRawPtr.is_null() {
raw_last_value = unsafe { &*last_segment.mToValue.mServo.mRawPtr };
Some(&**AnimationValue::as_arc(&raw_last_value))
} else {
None
};
let raw_to_value;
let keyframe_to_value = if !segment.mToValue.mServo.mRawPtr.is_null() {
raw_to_value = unsafe { &*segment.mToValue.mServo.mRawPtr };
Some(AnimationValue::as_arc(&raw_to_value))
} else {
None
};
// Composite with underlying value.
// A return value of None means, "Just use keyframe_value as-is."
let composite_endpoint = |keyframe_value: Option<&RawOffsetArc<AnimationValue>>,
composite_op: CompositeOperation| -> Option<AnimationValue> {
match keyframe_value {
Some(keyframe_value) => {
match composite_op {
CompositeOperation::Add => {
debug_assert!(need_underlying_value,
"Should have detected we need an underlying value");
underlying_value.as_ref().unwrap().animate(keyframe_value, Procedure::Add).ok()
},
CompositeOperation::Accumulate => {
debug_assert!(need_underlying_value,
"Should have detected we need an underlying value");
underlying_value
.as_ref()
.unwrap()
.animate(keyframe_value, Procedure::Accumulate { count: 1 })
.ok()
},
_ => None,
}
},
None => {
debug_assert!(need_underlying_value,
"Should have detected we need an underlying value");
underlying_value.clone()
},
}
};
let mut composited_from_value = composite_endpoint(keyframe_from_value, segment.mFromComposite);
let mut composited_to_value = composite_endpoint(keyframe_to_value, segment.mToComposite);
debug_assert!(keyframe_from_value.is_some() || composited_from_value.is_some(),
"Should have a suitable from value to use");
debug_assert!(keyframe_to_value.is_some() || composited_to_value.is_some(),
"Should have a suitable to value to use");
// Apply iteration composite behavior.
if iteration_composite == IterationCompositeOperation::Accumulate &&
computed_timing.mCurrentIteration > 0 {
let raw_last_value;
let last_value = if !last_segment.mToValue.mServo.mRawPtr.is_null() {
raw_last_value = unsafe { &*last_segment.mToValue.mServo.mRawPtr };
&*AnimationValue::as_arc(&raw_last_value)
} else {
debug_assert!(need_underlying_value,
"Should have detected we need an underlying value");
underlying_value.as_ref().unwrap()
};
// As with composite_endpoint, a return value of None means, "Use keyframe_value as-is."
let apply_iteration_composite = |keyframe_value: Option<&RawOffsetArc<AnimationValue>>,
composited_value: Option<AnimationValue>|
-> Option<AnimationValue> {
let count = computed_timing.mCurrentIteration;
match composited_value {
Some(endpoint) => {
last_value
.animate(&endpoint, Procedure::Accumulate { count })
.ok()
.or(Some(endpoint))
},
None => {
last_value
.animate(keyframe_value.unwrap(), Procedure::Accumulate { count })
.ok()
},
}
};
composited_from_value = apply_iteration_composite(keyframe_from_value,
composited_from_value);
composited_to_value = apply_iteration_composite(keyframe_to_value,
composited_to_value);
}
// Use the composited value if there is one, otherwise, use the original keyframe value.
let from_value = composited_from_value.as_ref().unwrap_or_else(|| keyframe_from_value.unwrap());
let to_value = composited_to_value.as_ref().unwrap_or_else(|| keyframe_to_value.unwrap());
let progress = unsafe { Gecko_GetProgressFromComputedTiming(computed_timing) };
if segment.mToKey == segment.mFromKey {
if progress < 0. {
value_map.insert(property, from_value.clone());
} else {
value_map.insert(property, to_value.clone());
}
return;
}
let pos = unsafe {
Gecko_GetPositionInSegment(segment, progress, computed_timing.mBeforeFlag)
};
if let Ok(value) = from_value.animate(to_value, Procedure::Interpolate { progress: pos }) {
value_map.insert(property, value);
} else if pos < 0.5 {
value_map.insert(property, from_value.clone());
let position = if segment.mToKey == segment.mFromKey {
// Note: compose_animation_segment doesn't use this value
// if segment.mFromKey == segment.mToKey, so assigning |progress| directly is fine.
progress
} else {
value_map.insert(property, to_value.clone());
}
unsafe { Gecko_GetPositionInSegment(segment, progress, computed_timing.mBeforeFlag) }
};
let result = compose_animation_segment(segment,
underlying_value.as_ref(),
last_value,
iteration_composite,
computed_timing.mCurrentIteration,
progress,
position);
value_map.insert(property, result);
}
macro_rules! get_property_id_from_nscsspropertyid {
@ -634,9 +692,9 @@ pub extern "C" fn Servo_Shorthand_AnimationValues_Serialize(shorthand_property:
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValue_GetOpacity(value: RawServoAnimationValueBorrowed)
-> f32
{
pub extern "C" fn Servo_AnimationValue_GetOpacity(
value: RawServoAnimationValueBorrowed
) -> f32 {
let value = AnimationValue::as_arc(&value);
if let AnimationValue::Opacity(opacity) = **value {
opacity
@ -646,9 +704,17 @@ pub extern "C" fn Servo_AnimationValue_GetOpacity(value: RawServoAnimationValueB
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValue_GetTransform(value: RawServoAnimationValueBorrowed,
list: *mut structs::RefPtr<nsCSSValueSharedList>)
{
pub extern "C" fn Servo_AnimationValue_Opacity(
opacity: f32
) -> RawServoAnimationValueStrong {
Arc::new(AnimationValue::Opacity(opacity)).into_strong()
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValue_GetTransform(
value: RawServoAnimationValueBorrowed,
list: *mut structs::RefPtr<nsCSSValueSharedList>
) {
let value = AnimationValue::as_arc(&value);
if let AnimationValue::Transform(ref servo_list) = **value {
let list = unsafe { &mut *list };
@ -665,6 +731,15 @@ pub extern "C" fn Servo_AnimationValue_GetTransform(value: RawServoAnimationValu
}
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValue_Transform(
list: *const nsCSSValueSharedList
) -> RawServoAnimationValueStrong {
let list = unsafe { (&*list).mHead.as_ref() };
let transform = style_structs::Box::clone_transform_from_list(list);
Arc::new(AnimationValue::Transform(transform)).into_strong()
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValue_DeepEqual(this: RawServoAnimationValueBorrowed,
other: RawServoAnimationValueBorrowed)