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

Don't rebuild the entire scene when scrolling with property bindings. #2043

Merged
merged 1 commit into from Nov 16, 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

Don't rebuild the entire scene when scrolling with property bindings.

Instead of rebuilding the entire scene / frame when scrolling while
property bindings are present, resolve the animated properties as
we prepare primitives and update the clip-scroll tree.

For transforms, reference frames now retain the property binding
information and build the correct transform in update_node(). We
could possibly optimize this in the future to not always rebuild
the reference frame transform, if there is no property binding active
on this reference frame, if it ever shows up in a profile.

For filters, rely on the new Picture tree functionality and resolve
the opacity filter value when preparing the picture primitive for
rendering. If the property resolves to an invisible filter, then
early out from this entire picture primitive.

Also update the animation example to demonstrate animating opacity.

Fixes #1921.
  • Loading branch information
gw3583 committed Nov 15, 2017
commit c53ed0c793c31cb88513d5e8da6d7726d561d6da
@@ -24,7 +24,9 @@ use webrender::api::*;

struct App {
property_key: PropertyBindingKey<LayoutTransform>,
opacity_key: PropertyBindingKey<f32>,
transform: LayoutTransform,
opacity: f32,
}

impl Example for App {
@@ -49,14 +51,18 @@ impl Example for App {
.. LayoutPrimitiveInfo::new(bounds)
};

let filters = vec![
FilterOp::Opacity(PropertyBinding::Binding(self.opacity_key), self.opacity),
];

builder.push_stacking_context(
&info,
ScrollPolicy::Scrollable,
Some(PropertyBinding::Binding(self.property_key)),
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
filters,
);

// Fill it with a white rect
@@ -68,18 +74,21 @@ impl Example for App {
fn on_event(&mut self, event: glutin::Event, api: &RenderApi, document_id: DocumentId) -> bool {
match event {
glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
let (offset_x, offset_y, angle) = match key {
glutin::VirtualKeyCode::Down => (0.0, 10.0, 0.0),
glutin::VirtualKeyCode::Up => (0.0, -10.0, 0.0),
glutin::VirtualKeyCode::Right => (10.0, 0.0, 0.0),
glutin::VirtualKeyCode::Left => (-10.0, 0.0, 0.0),
glutin::VirtualKeyCode::Comma => (0.0, 0.0, 0.1),
glutin::VirtualKeyCode::Period => (0.0, 0.0, -0.1),
let (offset_x, offset_y, angle, delta_opacity) = match key {
glutin::VirtualKeyCode::Down => (0.0, 10.0, 0.0, 0.0),
glutin::VirtualKeyCode::Up => (0.0, -10.0, 0.0, 0.0),
glutin::VirtualKeyCode::Right => (10.0, 0.0, 0.0, 0.0),
glutin::VirtualKeyCode::Left => (-10.0, 0.0, 0.0, 0.0),
glutin::VirtualKeyCode::Comma => (0.0, 0.0, 0.1, 0.0),
glutin::VirtualKeyCode::Period => (0.0, 0.0, -0.1, 0.0),
glutin::VirtualKeyCode::Z => (0.0, 0.0, 0.0, -0.1),
glutin::VirtualKeyCode::X => (0.0, 0.0, 0.0, 0.1),
_ => return false,
};
// Update the transform based on the keyboard input and push it to
// webrender using the generate_frame API. This will recomposite with
// the updated transform.
self.opacity += delta_opacity;
let new_transform = self.transform
.pre_rotate(0.0, 0.0, 1.0, Radians::new(angle))
.post_translate(LayoutVector3D::new(offset_x, offset_y, 0.0));
@@ -92,7 +101,12 @@ impl Example for App {
value: new_transform,
},
],
floats: vec![],
floats: vec![
PropertyValue {
key: self.opacity_key,
value: self.opacity,
}
],
}),
);
self.transform = new_transform;
@@ -107,7 +121,9 @@ impl Example for App {
fn main() {
let mut app = App {
property_key: PropertyBindingKey::new(42), // arbitrary magic number
opacity_key: PropertyBindingKey::new(43),
transform: LayoutTransform::create_translation(0.0, 0.0, 0.0),
opacity: 0.5,
};
boilerplate::main_wrapper(&mut app, None);
}
@@ -5,7 +5,7 @@
use api::{ClipId, DeviceIntRect, LayerPixel, LayerPoint, LayerRect, LayerSize};
use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, LayoutVector2D, PipelineId};
use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollSensitivity};
use api::{StickyOffsetBounds, WorldPoint};
use api::{LayoutTransform, PropertyBinding, StickyOffsetBounds, WorldPoint};
use clip::{ClipSourcesHandle, ClipStore};
use clip_scroll_tree::{CoordinateSystemId, TransformUpdateState};
use euclid::SideOffsets2D;
@@ -14,6 +14,7 @@ use gpu_cache::GpuCache;
use gpu_types::{ClipScrollNodeIndex, ClipScrollNodeData};
use render_task::{ClipChain, ClipChainNode, ClipWorkItem};
use resource_cache::ResourceCache;
use scene::SceneProperties;
use spring::{DAMPING, STIFFNESS, Spring};
use std::rc::Rc;
use util::{MatrixHelpers, MaxRect};
@@ -185,12 +186,16 @@ impl ClipScrollNode {
pub fn new_reference_frame(
parent_id: Option<ClipId>,
frame_rect: &LayerRect,
transform: &LayerToScrollTransform,
source_transform: Option<PropertyBinding<LayoutTransform>>,
source_perspective: Option<LayoutTransform>,
origin_in_parent_reference_frame: LayerVector2D,
pipeline_id: PipelineId,
) -> Self {
let identity = LayoutTransform::identity();
let info = ReferenceFrameInfo {
transform: *transform,
resolved_transform: LayerToScrollTransform::identity(),
source_transform: source_transform.unwrap_or(PropertyBinding::Value(identity)),
source_perspective: source_perspective.unwrap_or(identity),
origin_in_parent_reference_frame,
};
Self::new(pipeline_id, parent_id, frame_rect, NodeType::ReferenceFrame(info))
@@ -273,12 +278,13 @@ impl ClipScrollNode {
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
scene_properties: &SceneProperties,
) {
// We set this earlier so that we can use it before we have all the data necessary
// to populate the ClipScrollNodeData.
self.node_data_index = ClipScrollNodeIndex(node_data.len() as u32);

self.update_transform(state);
self.update_transform(state, scene_properties);
self.update_clip_work_item(
state,
device_pixel_ratio,
@@ -366,20 +372,33 @@ impl ClipScrollNode {
state.parent_clip_chain = self.clip_chain_node.clone();
}

pub fn update_transform(&mut self, state: &mut TransformUpdateState) {
pub fn update_transform(
&mut self,
state: &mut TransformUpdateState,
scene_properties: &SceneProperties,
) {
// We calculate this here to avoid a double-borrow later.
let sticky_offset = self.calculate_sticky_offset(
&state.nearest_scrolling_ancestor_offset,
&state.nearest_scrolling_ancestor_viewport,
);

let (local_transform, accumulated_scroll_offset) = match self.node_type {
NodeType::ReferenceFrame(ref info) => {
self.combined_local_viewport_rect = info.transform
NodeType::ReferenceFrame(ref mut info) => {
// Resolve the transform against any property bindings.
let source_transform = scene_properties.resolve_layout_transform(&info.source_transform);
info.resolved_transform = LayerToScrollTransform::create_translation(
info.origin_in_parent_reference_frame.x,
info.origin_in_parent_reference_frame.y,
0.0
).pre_mul(&source_transform)
.pre_mul(&info.source_perspective);

self.combined_local_viewport_rect = info.resolved_transform
.with_destination::<LayerPixel>()
.inverse_rect_footprint(&state.parent_combined_viewport_rect);
self.reference_frame_relative_scroll_offset = LayerVector2D::zero();
(info.transform, state.parent_accumulated_scroll_offset)
(info.resolved_transform, state.parent_accumulated_scroll_offset)
}
NodeType::Clip(_) | NodeType::ScrollFrame(_) => {
// Move the parent's viewport into the local space (of the node origin)
@@ -437,7 +456,7 @@ impl ClipScrollNode {
state.nearest_scrolling_ancestor_viewport
.translate(&info.origin_in_parent_reference_frame);

if !info.transform.preserves_2d_axis_alignment() {
if !info.resolved_transform.preserves_2d_axis_alignment() {
state.current_coordinate_system_id = state.next_coordinate_system_id;
state.next_coordinate_system_id = state.next_coordinate_system_id.next();
}
@@ -788,7 +807,14 @@ impl ScrollingState {
pub struct ReferenceFrameInfo {
/// The transformation that establishes this reference frame, relative to the parent
/// reference frame. The origin of the reference frame is included in the transformation.
pub transform: LayerToScrollTransform,
pub resolved_transform: LayerToScrollTransform,

/// The source transform and perspective matrices provided by the stacking context
/// that forms this reference frame. We maintain the property binding information
/// here so that we can resolve the animated transform and update the tree each
/// frame.
pub source_transform: PropertyBinding<LayoutTransform>,
pub source_perspective: LayoutTransform,

/// The original, not including the transform and relative to the parent reference frame,
/// origin of this reference frame. This is already rolled into the `transform' property, but
@@ -2,9 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use api::{ClipId, DeviceIntRect, LayerPoint, LayerRect, LayerToScrollTransform};
use api::{ClipId, DeviceIntRect, LayerPoint, LayerRect};
use api::{LayerToWorldTransform, LayerVector2D, PipelineId, ScrollClamping, ScrollEventPhase};
use api::{ScrollLayerState, ScrollLocation, WorldPoint};
use api::{PropertyBinding, LayoutTransform, ScrollLayerState, ScrollLocation, WorldPoint};
use clip::ClipStore;
use clip_scroll_node::{ClipScrollNode, NodeType, ScrollingState, StickyFrameInfo};
use gpu_cache::GpuCache;
@@ -13,6 +13,7 @@ use internal_types::{FastHashMap, FastHashSet};
use print_tree::{PrintTree, PrintTreePrinter};
use render_task::ClipChain;
use resource_cache::ResourceCache;
use scene::SceneProperties;

pub type ScrollStates = FastHashMap<ClipId, ScrollingState>;

@@ -333,6 +334,7 @@ impl ClipScrollTree {
gpu_cache: &mut GpuCache,
pan: LayerPoint,
node_data: &mut Vec<ClipScrollNodeData>,
scene_properties: &SceneProperties,
) {
if self.nodes.is_empty() {
return;
@@ -364,6 +366,7 @@ impl ClipScrollTree {
resource_cache,
gpu_cache,
node_data,
scene_properties,
);
}

@@ -376,6 +379,7 @@ impl ClipScrollTree {
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
node_data: &mut Vec<ClipScrollNodeData>,
scene_properties: &SceneProperties,
) {
// TODO(gw): This is an ugly borrow check workaround to clone these.
// Restructure this to avoid the clones!
@@ -393,6 +397,7 @@ impl ClipScrollTree {
clip_store,
resource_cache,
gpu_cache,
scene_properties,
);

node.children.clone()
@@ -407,6 +412,7 @@ impl ClipScrollTree {
resource_cache,
gpu_cache,
node_data,
scene_properties,
);
}
}
@@ -440,7 +446,8 @@ impl ClipScrollTree {
pub fn add_reference_frame(
&mut self,
rect: &LayerRect,
transform: &LayerToScrollTransform,
source_transform: Option<PropertyBinding<LayoutTransform>>,
source_perspective: Option<LayoutTransform>,
origin_in_parent_reference_frame: LayerVector2D,
pipeline_id: PipelineId,
parent_id: Option<ClipId>,
@@ -455,7 +462,8 @@ impl ClipScrollTree {
let node = ClipScrollNode::new_reference_frame(
parent_id,
rect,
transform,
source_transform,
source_perspective,
origin_in_parent_reference_frame,
pipeline_id,
);
@@ -515,7 +523,7 @@ impl ClipScrollTree {
pt.end_level();
}
NodeType::ReferenceFrame(ref info) => {
pt.new_level(format!("ReferenceFrame {:?}", info.transform));
pt.new_level(format!("ReferenceFrame {:?}", info.resolved_transform));
pt.add_item(format!("id: {:?}", id));
}
NodeType::ScrollFrame(scrolling_info) => {
@@ -6,8 +6,8 @@
use api::{BuiltDisplayListIter, ClipAndScrollInfo, ClipId, ColorF, ComplexClipRegion};
use api::{DeviceUintRect, DeviceUintSize, DisplayItemRef, Epoch, FilterOp};
use api::{ImageDisplayItem, ItemRange, LayerPoint, LayerPrimitiveInfo, LayerRect};
use api::{LayerSize, LayerToScrollTransform, LayerVector2D};
use api::{LayoutRect, LayoutSize, LayoutTransform};
use api::{LayerSize, LayerVector2D};
use api::{LayoutRect, LayoutSize};
use api::{LocalClip, PipelineId, ScrollClamping, ScrollEventPhase, ScrollLayerState};
use api::{ScrollLocation, ScrollPolicy, ScrollSensitivity, SpecificDisplayItem, StackingContext};
use api::{ClipMode, TileOffset, TransformStyle, WorldPoint};
@@ -21,7 +21,7 @@ use internal_types::{FastHashMap, FastHashSet, RendererFrame};
use prim_store::RectangleContent;
use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters};
use resource_cache::{FontInstanceMap,ResourceCache, TiledImageMap};
use scene::{Scene, StackingContextHelpers, ScenePipeline};
use scene::{Scene, StackingContextHelpers, ScenePipeline, SceneProperties};
use tiling::{CompositeOps, Frame};
use util::ComplexClipRegionHelpers;

@@ -246,7 +246,6 @@ impl<'a> FlattenContext<'a> {
stacking_context.filter_ops_for_compositing(
display_list,
filters,
&self.scene.properties,
),
stacking_context.mix_blend_mode_for_compositing(),
)
@@ -264,23 +263,15 @@ impl<'a> FlattenContext<'a> {
let is_reference_frame =
stacking_context.transform.is_some() || stacking_context.perspective.is_some();
if is_reference_frame {
let transform = stacking_context.transform.as_ref();
let transform = self.scene.properties.resolve_layout_transform(transform);
let perspective = stacking_context
.perspective
.unwrap_or_else(LayoutTransform::identity);
let origin = reference_frame_relative_offset + bounds.origin.to_vector();
let transform = LayerToScrollTransform::create_translation(origin.x, origin.y, 0.0)
.pre_mul(&transform)
.pre_mul(&perspective);

let reference_frame_bounds = LayerRect::new(LayerPoint::zero(), bounds.size);
let mut clip_id = self.apply_scroll_frame_id_replacement(context_scroll_node_id);
clip_id = self.builder.push_reference_frame(
Some(clip_id),
pipeline_id,
&reference_frame_bounds,
&transform,
stacking_context.transform,
stacking_context.perspective,
origin,
false,
self.clip_scroll_tree,
@@ -356,12 +347,12 @@ impl<'a> FlattenContext<'a> {

let iframe_rect = LayerRect::new(LayerPoint::zero(), bounds.size);
let origin = reference_frame_relative_offset + bounds.origin.to_vector();
let transform = LayerToScrollTransform::create_translation(origin.x, origin.y, 0.0);
let iframe_reference_frame_id = self.builder.push_reference_frame(
Some(clip_id),
pipeline_id,
&iframe_rect,
&transform,
None,
None,
origin,
true,
self.clip_scroll_tree,
@@ -1196,6 +1187,7 @@ impl FrameContext {
pan: LayerPoint,
texture_cache_profile: &mut TextureCacheProfileCounters,
gpu_cache_profile: &mut GpuCacheProfileCounters,
scene_properties: &SceneProperties,
) -> RendererFrame {
let frame = frame_builder.build(
resource_cache,
@@ -1207,6 +1199,7 @@ impl FrameContext {
pan,
texture_cache_profile,
gpu_cache_profile,
scene_properties,
);

self.get_renderer_frame_impl(Some(frame))
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.