Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement an API that allows animating certain properties of an existing display list. #832

Merged
merged 1 commit into from Feb 8, 2017
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Implement an API that allows animating certain properties of

an existing display list.

Currently, this allows animating the transform and opacity of
a stacking context, but it can easily be extended in the future
to handle other property types (e.g. other filters etc).

The current list of property values are passed via the
GenerateFrame message, which ensures that all animated properties
are updated atomically.

The implementation right now is far from optimal. The idea here
is to get the public API correct and implemented, and then we can
focus on optimizing the implementation, once the in-progress work
on scroll layers and reference frames lands.
  • Loading branch information
gw3583 committed Feb 7, 2017
commit 82457e0851d9ed4ccfa672bb246912db90218eac
@@ -112,8 +112,8 @@ fn main() {
bounds,
clip_region,
0,
&LayoutTransform::identity(),
&LayoutTransform::identity(),
LayoutTransform::identity().into(),
LayoutTransform::identity(),
webrender_traits::MixBlendMode::Normal,
Vec::new());

@@ -235,7 +235,7 @@ fn main() {
builder,
true);
api.set_root_pipeline(pipeline_id);
api.generate_frame();
api.generate_frame(None);

for event in window.wait_events() {
renderer.update();
@@ -10,7 +10,7 @@ use internal_types::{RendererFrame};
use frame_builder::{FrameBuilder, FrameBuilderConfig};
use layer::Layer;
use resource_cache::ResourceCache;
use scene::Scene;
use scene::{Scene, SceneProperties};
use scroll_tree::{ScrollTree, ScrollStates};
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
@@ -58,7 +58,9 @@ impl DisplayListHelpers for Vec<DisplayItem> {

trait StackingContextHelpers {
fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode>;
fn filter_ops_for_compositing(&self, auxiliary_lists: &AuxiliaryLists) -> Vec<LowLevelFilterOp>;
fn filter_ops_for_compositing(&self,
auxiliary_lists: &AuxiliaryLists,
properties: &SceneProperties) -> Vec<LowLevelFilterOp>;
}

impl StackingContextHelpers for StackingContext {
@@ -69,7 +71,9 @@ impl StackingContextHelpers for StackingContext {
}
}

fn filter_ops_for_compositing(&self, auxiliary_lists: &AuxiliaryLists) -> Vec<LowLevelFilterOp> {
fn filter_ops_for_compositing(&self,
auxiliary_lists: &AuxiliaryLists,
properties: &SceneProperties) -> Vec<LowLevelFilterOp> {
let mut filters = vec![];
for filter in auxiliary_lists.filters(&self.filters) {
match *filter {
@@ -102,7 +106,8 @@ impl StackingContextHelpers for StackingContext {
filters.push(
LowLevelFilterOp::Invert(Au::from_f32_px(amount)));
}
FilterOp::Opacity(amount) => {
FilterOp::Opacity(ref value) => {
let amount = properties.resolve_float(value, 1.0);
filters.push(
LowLevelFilterOp::Opacity(Au::from_f32_px(amount)));
}
@@ -364,7 +369,7 @@ impl Frame {
.get(&pipeline_id)
.expect("No auxiliary lists?!");
CompositeOps::new(
stacking_context.filter_ops_for_compositing(auxiliary_lists),
stacking_context.filter_ops_for_compositing(auxiliary_lists, &context.scene.properties),
stacking_context.mix_blend_mode_for_compositing())
};

@@ -373,11 +378,15 @@ impl Frame {
return;
}

let stacking_context_transform = context.scene
.properties
.resolve_layout_transform(&stacking_context.transform);

let mut transform =
layer_relative_transform.pre_translated(stacking_context.bounds.origin.x,
stacking_context.bounds.origin.y,
0.0)
.pre_mul(&stacking_context.transform)
.pre_mul(&stacking_context_transform)
.pre_mul(&stacking_context.perspective);

let mut reference_frame_id = current_reference_frame_id;
@@ -388,7 +397,7 @@ impl Frame {

// If we have a transformation, we establish a new reference frame. This means
// that fixed position stacking contexts are positioned relative to us.
if stacking_context.transform != LayoutTransform::identity() ||
if stacking_context_transform != LayoutTransform::identity() ||
stacking_context.perspective != LayoutTransform::identity() {
scroll_layer_id = self.scroll_tree.add_reference_frame(clip_region.main,
transform,
@@ -68,7 +68,7 @@ pub fn should_record_msg(msg: &ApiMsg) -> bool {
&ApiMsg::AddRawFont(..) |
&ApiMsg::AddNativeFont(..) |
&ApiMsg::AddImage(..) |
&ApiMsg::GenerateFrame |
&ApiMsg::GenerateFrame(..) |
&ApiMsg::UpdateImage(..) |
&ApiMsg::DeleteImage(..) |
&ApiMsg::SetRootDisplayList(..) |
@@ -316,7 +316,22 @@ impl RenderBackend {
ApiMsg::VRCompositorCommand(context_id, command) => {
self.handle_vr_compositor_command(context_id, command);
}
ApiMsg::GenerateFrame => {
ApiMsg::GenerateFrame(property_bindings) => {
// Ideally, when there are property bindings present,
// we won't need to rebuild the entire frame here.
// However, to avoid conflicts with the ongoing work to
// refactor how scroll roots + transforms work, this
// just rebuilds the frame if there are animated property
// bindings present for now.
// TODO(gw): Once the scrolling / reference frame changes
// are completed, optimize the internals of
// animated properties to not require a full
// rebuild of the frame!
if let Some(property_bindings) = property_bindings {
self.scene.properties.set_properties(property_bindings);
self.build_scene();
}

let frame = profile_counters.total_time.profile(|| {
self.render()
});
@@ -7,7 +7,71 @@ use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use tiling::AuxiliaryListsMap;
use webrender_traits::{AuxiliaryLists, BuiltDisplayList, PipelineId, Epoch, ColorF};
use webrender_traits::{DisplayItem, LayerSize};
use webrender_traits::{DisplayItem, DynamicProperties, LayerSize, LayoutTransform};
use webrender_traits::{PropertyBinding, PropertyBindingId};

/// Stores a map of the animated property bindings for the current display list. These
/// can be used to animate the transform and/or opacity of a display list without
/// re-submitting the display list itself.
pub struct SceneProperties {
transform_properties: HashMap<PropertyBindingId, LayoutTransform>,
float_properties: HashMap<PropertyBindingId, f32>,
}

impl SceneProperties {
pub fn new() -> SceneProperties {
SceneProperties {
transform_properties: HashMap::with_hasher(Default::default()),
float_properties: HashMap::with_hasher(Default::default()),
}
}

/// Set the current property list for this display list.
pub fn set_properties(&mut self, properties: DynamicProperties) {
self.transform_properties.clear();
self.float_properties.clear();

for property in properties.transforms {
self.transform_properties.insert(property.key.id, property.value);
}

for property in properties.floats {
self.float_properties.insert(property.key.id, property.value);
}
}

/// Get the current value for a transform property.
pub fn resolve_layout_transform(&self, property: &PropertyBinding<LayoutTransform>) -> LayoutTransform {
match *property {
PropertyBinding::Value(matrix) => matrix,
PropertyBinding::Binding(ref key) => {
self.transform_properties
.get(&key.id)
.cloned()
.unwrap_or_else(|| {
warn!("Property binding {:?} has an invalid value.", key);
LayoutTransform::identity()
})
}
}
}

/// Get the current value for a float property.
pub fn resolve_float(&self, property: &PropertyBinding<f32>, default_value: f32) -> f32 {
match *property {
PropertyBinding::Value(value) => value,
PropertyBinding::Binding(ref key) => {
self.float_properties
.get(&key.id)
.cloned()
.unwrap_or_else(|| {
warn!("Property binding {:?} has an invalid value.", key);
default_value
})
}
}
}
}

/// A representation of the layout within the display port for a given document or iframe.
#[derive(Debug)]
@@ -24,6 +88,7 @@ pub struct Scene {
pub pipeline_map: HashMap<PipelineId, ScenePipeline, BuildHasherDefault<FnvHasher>>,
pub pipeline_auxiliary_lists: AuxiliaryListsMap,
pub display_lists: HashMap<PipelineId, Vec<DisplayItem>, BuildHasherDefault<FnvHasher>>,
pub properties: SceneProperties,
}

impl Scene {
@@ -33,6 +98,7 @@ impl Scene {
pipeline_map: HashMap::with_hasher(Default::default()),
pipeline_auxiliary_lists: HashMap::with_hasher(Default::default()),
display_lists: HashMap::with_hasher(Default::default()),
properties: SceneProperties::new(),
}
}

@@ -10,9 +10,10 @@ use {ApiMsg, ColorF, DisplayListBuilder, Epoch, ImageDescriptor};
use {FontKey, IdNamespace, ImageKey, NativeFontHandle, PipelineId};
use {RenderApiSender, ResourceId, ScrollEventPhase, ScrollLayerState, ScrollLocation, ServoScrollRootId};
use {GlyphKey, GlyphDimensions, ImageData, WebGLContextId, WebGLCommand};
use {DeviceIntSize, LayoutPoint, LayoutSize, WorldPoint};
use {DeviceIntSize, DynamicProperties, LayoutPoint, LayoutSize, WorldPoint, PropertyBindingKey, PropertyBindingId};
use VRCompositorCommand;
use ExternalEvent;
use std::marker::PhantomData;

impl RenderApiSender {
pub fn new(api_sender: MsgSender<ApiMsg>,
@@ -233,8 +234,11 @@ impl RenderApi {
self.api_sender.send(msg).unwrap();
}

pub fn generate_frame(&self) {
let msg = ApiMsg::GenerateFrame;
/// Generate a new frame. Optionally, supply a list of animated
/// property bindings that should be used to resolve bindings
/// in the current display list.
pub fn generate_frame(&self, property_bindings: Option<DynamicProperties>) {
let msg = ApiMsg::GenerateFrame(property_bindings);
self.api_sender.send(msg).unwrap();
}

@@ -252,6 +256,19 @@ impl RenderApi {
self.api_sender.send(ApiMsg::ShutDown).unwrap();
}

/// Create a new unique key that can be used for
/// animated property bindings.
pub fn generate_property_binding_key<T: Copy>(&self) -> PropertyBindingKey<T> {
let new_id = self.next_unique_id();
PropertyBindingKey {
id: PropertyBindingId {
namespace: new_id.0,
uid: new_id.1,
},
_phantom: PhantomData,
}
}

#[inline]
fn next_unique_id(&self) -> (u32, u32) {
let IdNamespace(namespace) = self.id_namespace;
@@ -15,7 +15,7 @@ use {PushScrollLayerItem, PushStackingContextDisplayItem, RectangleDisplayItem,
use {ScrollPolicy, ServoScrollRootId, SpecificDisplayItem, StackingContext, TextDisplayItem};
use {WebGLContextId, WebGLDisplayItem, YuvImageDisplayItem};
use {LayoutTransform, LayoutPoint, LayoutRect, LayoutSize};
use {GlyphOptions};
use {GlyphOptions, PropertyBinding};

impl BuiltDisplayListDescriptor {
pub fn size(&self) -> usize {
@@ -292,16 +292,16 @@ impl DisplayListBuilder {
bounds: LayoutRect,
clip: ClipRegion,
z_index: i32,
transform: &LayoutTransform,
perspective: &LayoutTransform,
transform: PropertyBinding<LayoutTransform>,
perspective: LayoutTransform,
mix_blend_mode: MixBlendMode,
filters: Vec<FilterOp>) {
let stacking_context = StackingContext {
scroll_policy: scroll_policy,
bounds: bounds,
z_index: z_index,
transform: transform.clone(),
perspective: perspective.clone(),
transform: transform,
perspective: perspective,
mix_blend_mode: mix_blend_mode,
filters: self.auxiliary_lists_builder.add_filters(&filters),
};
@@ -4,14 +4,14 @@

use display_list::AuxiliaryListsBuilder;
use {FilterOp, MixBlendMode, ScrollPolicy, StackingContext};
use {LayoutTransform, LayoutRect};
use {LayoutTransform, LayoutRect, PropertyBinding};

impl StackingContext {
pub fn new(scroll_policy: ScrollPolicy,
bounds: LayoutRect,
z_index: i32,
transform: &LayoutTransform,
perspective: &LayoutTransform,
transform: PropertyBinding<LayoutTransform>,
perspective: LayoutTransform,
mix_blend_mode: MixBlendMode,
filters: Vec<FilterOp>,
auxiliary_lists_builder: &mut AuxiliaryListsBuilder)
@@ -20,8 +20,8 @@ impl StackingContext {
scroll_policy: scroll_policy,
bounds: bounds,
z_index: z_index,
transform: transform.clone(),
perspective: perspective.clone(),
transform: transform,
perspective: perspective,
mix_blend_mode: mix_blend_mode,
filters: auxiliary_lists_builder.add_filters(&filters),
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.