Layerize canvas

Note that this keeps using readback right now, `NativeSurface` painting
will be implemented soon.

Also see https://github.com/servo/servo/issues/6142
This commit is contained in:
ecoal95 2015-05-20 10:29:08 +02:00
parent 6481058309
commit 3350522306
35 changed files with 769 additions and 500 deletions

View file

@ -13,9 +13,15 @@ git = "https://github.com/servo/rust-azure"
[dependencies.canvas]
path = "../canvas"
[dependencies.canvas_traits]
path = "../canvas_traits"
[dependencies.gfx]
path = "../gfx"
[dependencies.gfx_traits]
path = "../gfx_traits"
[dependencies.msg]
path = "../msg"

View file

@ -8,11 +8,12 @@
use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
use canvas_traits::CanvasMsg;
use msg::compositor_msg::LayerId;
use geom::{Rect, Size2D};
use gfx::display_list::OpaqueNode;
use gfx::font_cache_task::FontCacheTask;
use gfx::font_context::FontContext;
use msg::compositor_msg::LayerId;
use msg::constellation_msg::ConstellationChan;
use net_traits::image::base::Image;
use net_traits::image_cache_task::{ImageCacheChan, ImageCacheTask, ImageState};
@ -22,7 +23,7 @@ use std::cell::Cell;
use std::collections::HashMap;
use std::collections::hash_state::DefaultState;
use std::ptr;
use std::sync::Arc;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Sender};
use style::selector_matching::Stylist;
use url::Url;
@ -103,6 +104,9 @@ pub struct SharedLayoutContext {
/// sent.
pub new_animations_sender: Sender<Animation>,
/// A channel to send canvas renderers to paint task, in order to correctly paint the layers
pub canvas_layers_sender: Sender<(LayerId, Option<Arc<Mutex<Sender<CanvasMsg>>>>)>,
/// The visible rects for each layer, as reported to us by the compositor.
pub visible_rects: Arc<HashMap<LayerId, Rect<Au>, DefaultState<FnvHasher>>>,

View file

@ -12,7 +12,6 @@
use azure::azure_hl::Color;
use block::BlockFlow;
use canvas::canvas_msg::{CanvasMsg, CanvasCommonMsg};
use context::LayoutContext;
use flow::{self, BaseFlow, Flow, IS_ABSOLUTELY_POSITIONED, NEEDS_LAYER};
use fragment::{CoordinateSystem, Fragment, IframeFragmentInfo, ImageFragmentInfo};
@ -23,7 +22,7 @@ use model::{self, MaybeAuto, ToGfxMatrix};
use table_cell::CollapsedBordersForCell;
use geom::{Matrix2D, Point2D, Rect, Size2D, SideOffsets2D};
use gfx::color;
use gfx_traits::color;
use gfx::display_list::{BLUR_INFLATION_FACTOR, BaseDisplayItem, BorderDisplayItem};
use gfx::display_list::{BorderRadii, BoxShadowClipMode, BoxShadowDisplayItem, ClippingRegion};
use gfx::display_list::{DisplayItem, DisplayList, DisplayItemMetadata};
@ -32,7 +31,7 @@ use gfx::display_list::{GradientStop, ImageDisplayItem, LineDisplayItem};
use gfx::display_list::{OpaqueNode, SolidColorDisplayItem};
use gfx::display_list::{StackingContext, TextDisplayItem, TextOrientation};
use gfx::paint_task::{PaintLayer, THREAD_TINT_COLORS};
use msg::compositor_msg::ScrollPolicy;
use msg::compositor_msg::{ScrollPolicy, LayerId};
use msg::constellation_msg::ConstellationChan;
use msg::constellation_msg::Msg as ConstellationMsg;
use png::{self, PixelsByColorType};
@ -40,7 +39,6 @@ use std::cmp;
use std::default::Default;
use std::iter::repeat;
use std::sync::Arc;
use std::sync::mpsc::channel;
use style::computed_values::filter::Filter;
use style::computed_values::transform::ComputedMatrix;
use style::computed_values::{background_attachment, background_clip, background_origin, background_repeat, background_size};
@ -56,6 +54,15 @@ use util::geometry::{Au, ZERO_POINT};
use util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
use util::opts;
use canvas_traits::{CanvasMsg, CanvasCommonMsg};
use std::sync::mpsc::channel;
/// A possible `PaintLayer` for an stacking context
pub enum StackingContextLayer {
Existing(PaintLayer),
IfCanvas(LayerId),
}
/// The results of display list building for a single flow.
pub enum DisplayListBuildingResult {
None,
@ -240,7 +247,8 @@ pub trait FragmentDisplayListBuilding {
fn create_stacking_context(&self,
base_flow: &BaseFlow,
display_list: Box<DisplayList>,
layer: Option<Arc<PaintLayer>>)
layout_context: &LayoutContext,
layer: StackingContextLayer)
-> Arc<StackingContext>;
}
@ -1077,11 +1085,11 @@ impl FragmentDisplayListBuilding for Fragment {
}
}
SpecificFragmentInfo::Canvas(ref canvas_fragment_info) => {
// TODO(ecoal95): make the canvas with a renderer use the custom layer
let width = canvas_fragment_info.replaced_image_fragment_info
.computed_inline_size.map_or(0, |w| w.to_px() as usize);
let height = canvas_fragment_info.replaced_image_fragment_info
.computed_block_size.map_or(0, |h| h.to_px() as usize);
let (sender, receiver) = channel::<Vec<u8>>();
let canvas_data = match canvas_fragment_info.renderer {
Some(ref renderer) => {
@ -1091,12 +1099,11 @@ impl FragmentDisplayListBuilding for Fragment {
},
None => repeat(0xFFu8).take(width * height * 4).collect(),
};
let canvas_display_item = box ImageDisplayItem {
display_list.content.push_back(DisplayItem::ImageClass(box ImageDisplayItem{
base: BaseDisplayItem::new(stacking_relative_content_box,
DisplayItemMetadata::new(self.node,
&*self.style,
Cursor::DefaultCursor),
&*self.style,
Cursor::DefaultCursor),
(*clip).clone()),
image: Arc::new(png::Image {
width: width as u32,
@ -1105,9 +1112,7 @@ impl FragmentDisplayListBuilding for Fragment {
}),
stretch_size: stacking_relative_content_box.size,
image_rendering: image_rendering::T::Auto,
};
display_list.content.push_back(DisplayItem::ImageClass(canvas_display_item));
}));
}
SpecificFragmentInfo::UnscannedText(_) => {
panic!("Shouldn't see unscanned fragments here.")
@ -1121,7 +1126,8 @@ impl FragmentDisplayListBuilding for Fragment {
fn create_stacking_context(&self,
base_flow: &BaseFlow,
display_list: Box<DisplayList>,
layer: Option<Arc<PaintLayer>>)
layout_context: &LayoutContext,
layer: StackingContextLayer)
-> Arc<StackingContext> {
let border_box = self.stacking_relative_border_box(&base_flow.stacking_relative_position,
&base_flow.absolute_position_info
@ -1153,6 +1159,28 @@ impl FragmentDisplayListBuilding for Fragment {
filters.push(Filter::Opacity(effects.opacity))
}
// Ensure every canvas has a layer
let layer = match layer {
StackingContextLayer::Existing(existing_layer) => Some(existing_layer),
StackingContextLayer::IfCanvas(layer_id) => {
if let SpecificFragmentInfo::Canvas(_) = self.specific {
Some(PaintLayer::new(layer_id, color::transparent(), ScrollPolicy::Scrollable))
} else {
None
}
}
};
// If it's a canvas we must propagate the layer and the renderer to the paint
// task
if let SpecificFragmentInfo::Canvas(ref fragment_info) = self.specific {
let layer_id = layer.as_ref().unwrap().id;
layout_context.shared.canvas_layers_sender
.send((layer_id, fragment_info.renderer.clone())).unwrap();
}
let layer = layer.map(|l| Arc::new(l));
Arc::new(StackingContext::new(display_list,
&border_box,
&overflow,
@ -1419,10 +1447,11 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
background_border_level);
self.base.display_list_building_result = if self.fragment.establishes_stacking_context() {
DisplayListBuildingResult::StackingContext(self.fragment.create_stacking_context(
&self.base,
display_list,
None))
DisplayListBuildingResult::StackingContext(
self.fragment.create_stacking_context(&self.base,
display_list,
layout_context,
StackingContextLayer::IfCanvas(self.layer_id(0))))
} else {
match self.fragment.style.get_box().position {
position::T::static_ => {}
@ -1452,10 +1481,11 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
if !self.will_get_layer() {
// We didn't need a layer.
self.base.display_list_building_result =
DisplayListBuildingResult::StackingContext(self.fragment.create_stacking_context(
&self.base,
display_list,
None));
DisplayListBuildingResult::StackingContext(
self.fragment.create_stacking_context(&self.base,
display_list,
layout_context,
StackingContextLayer::IfCanvas(self.layer_id(0))));
return
}
@ -1466,11 +1496,11 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
ScrollPolicy::Scrollable
};
let transparent = color::transparent();
let stacking_context = self.fragment.create_stacking_context(
&self.base,
display_list,
Some(Arc::new(PaintLayer::new(self.layer_id(0), transparent, scroll_policy))));
let paint_layer = PaintLayer::new(self.layer_id(0), color::transparent(), scroll_policy);
let stacking_context = self.fragment.create_stacking_context(&self.base,
display_list,
layout_context,
StackingContextLayer::Existing(paint_layer));
self.base.display_list_building_result =
DisplayListBuildingResult::StackingContext(stacking_context)
}
@ -1487,7 +1517,10 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
self.base.display_list_building_result = if self.fragment.establishes_stacking_context() {
DisplayListBuildingResult::StackingContext(
self.fragment.create_stacking_context(&self.base, display_list, None))
self.fragment.create_stacking_context(&self.base,
display_list,
layout_context,
StackingContextLayer::IfCanvas(self.layer_id(0))))
} else {
DisplayListBuildingResult::Normal(display_list)
}
@ -1544,6 +1577,7 @@ impl InlineFlowDisplayListBuilding for InlineFlow {
&self.base.stacking_relative_position_of_display_port);
has_stacking_context = fragment.establishes_stacking_context();
match fragment.specific {
SpecificFragmentInfo::InlineBlock(ref mut block_flow) => {
let block_flow = &mut *block_flow.flow_ref;
@ -1571,17 +1605,23 @@ impl InlineFlowDisplayListBuilding for InlineFlow {
// FIXME(Savago): fix Fragment::establishes_stacking_context() for absolute positioned item
// and remove the check for filter presence. Further details on #5812.
if has_stacking_context &&
!self.fragments.fragments[0].style().get_effects().filter.is_empty() {
self.base.display_list_building_result =
DisplayListBuildingResult::StackingContext(
self.fragments.fragments[0].create_stacking_context(&self.base,
display_list,
None));
has_stacking_context = has_stacking_context && {
if let SpecificFragmentInfo::Canvas(_) = self.fragments.fragments[0].specific {
true
} else {
!self.fragments.fragments[0].style().get_effects().filter.is_empty()
}
};
self.base.display_list_building_result = if has_stacking_context {
DisplayListBuildingResult::StackingContext(
self.fragments.fragments[0].create_stacking_context(&self.base,
display_list,
layout_context,
StackingContextLayer::IfCanvas(self.layer_id(0))))
} else {
self.base.display_list_building_result =
DisplayListBuildingResult::Normal(display_list);
}
DisplayListBuildingResult::Normal(display_list)
};
if opts::get().validate_display_list_geometry {
self.base.validate_display_list_geometry();

View file

@ -6,7 +6,7 @@
#![deny(unsafe_code)]
use canvas::canvas_msg::CanvasMsg;
use canvas_traits::CanvasMsg;
use css::node_style::StyledNode;
use context::LayoutContext;
use floats::ClearType;
@ -195,9 +195,7 @@ impl SpecificFragmentInfo {
SpecificFragmentInfo::Iframe(_) => "SpecificFragmentInfo::Iframe",
SpecificFragmentInfo::Image(_) => "SpecificFragmentInfo::Image",
SpecificFragmentInfo::InlineAbsolute(_) => "SpecificFragmentInfo::InlineAbsolute",
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => {
"SpecificFragmentInfo::InlineAbsoluteHypothetical"
}
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => "SpecificFragmentInfo::InlineAbsoluteHypothetical",
SpecificFragmentInfo::InlineBlock(_) => "SpecificFragmentInfo::InlineBlock",
SpecificFragmentInfo::ScannedText(_) => "SpecificFragmentInfo::ScannedText",
SpecificFragmentInfo::Table => "SpecificFragmentInfo::Table",
@ -1993,6 +1991,13 @@ impl Fragment {
if self.style().get_effects().transform.is_some() {
return true
}
// Canvas always layerizes, as an special case
// FIXME(pcwalton): Don't unconditionally form stacking contexts for each canvas.
if let SpecificFragmentInfo::Canvas(_) = self.specific {
return true
}
match self.style().get_box().position {
position::T::absolute | position::T::fixed => {
// FIXME(pcwalton): This should only establish a new stacking context when

View file

@ -24,6 +24,7 @@ use sequential;
use wrapper::{LayoutNode, TLayoutNode};
use azure::azure::AzColor;
use canvas_traits::CanvasMsg;
use encoding::EncodingRef;
use encoding::all::UTF_8;
use geom::matrix2d::Matrix2D;
@ -31,7 +32,7 @@ use geom::point::Point2D;
use geom::rect::Rect;
use geom::scale_factor::ScaleFactor;
use geom::size::Size2D;
use gfx::color;
use gfx_traits::color;
use gfx::display_list::{ClippingRegion, DisplayItemMetadata, DisplayList, OpaqueNode};
use gfx::display_list::{StackingContext};
use gfx::font_cache_task::FontCacheTask;
@ -39,7 +40,7 @@ use gfx::paint_task::Msg as PaintMsg;
use gfx::paint_task::{PaintChan, PaintLayer};
use layout_traits::{LayoutControlMsg, LayoutTaskFactory};
use log;
use msg::compositor_msg::{Epoch, LayerId, ScrollPolicy};
use msg::compositor_msg::{Epoch, ScrollPolicy, LayerId};
use msg::constellation_msg::Msg as ConstellationMsg;
use msg::constellation_msg::{ConstellationChan, Failure, PipelineExitType, PipelineId};
use profile_traits::mem::{self, Report, ReportsChan};
@ -134,7 +135,7 @@ pub struct LayoutTaskData {
/// sent.
pub new_animations_sender: Sender<Animation>,
/// A counter for epoch messages.
/// A counter for epoch messages
epoch: Epoch,
/// The position and size of the visible rect for each layer. We do not build display lists
@ -195,6 +196,11 @@ pub struct LayoutTask {
/// Is this the first reflow in this LayoutTask?
pub first_reflow: Cell<bool>,
/// To receive a canvas renderer associated to a layer, this message is propagated
/// to the paint chan
pub canvas_layers_receiver: Receiver<(LayerId, Option<Arc<Mutex<Sender<CanvasMsg>>>>)>,
pub canvas_layers_sender: Sender<(LayerId, Option<Arc<Mutex<Sender<CanvasMsg>>>>)>,
/// A mutex to allow for fast, read-only RPC of layout's internal data
/// structures, while still letting the LayoutTask modify them.
///
@ -310,6 +316,7 @@ impl LayoutTask {
// Create the channel on which new animations can be sent.
let (new_animations_sender, new_animations_receiver) = channel();
let (image_cache_sender, image_cache_receiver) = channel();
let (canvas_layers_sender, canvas_layers_receiver) = channel();
LayoutTask {
id: id,
@ -329,6 +336,8 @@ impl LayoutTask {
first_reflow: Cell::new(true),
image_cache_receiver: image_cache_receiver,
image_cache_sender: ImageCacheChan(image_cache_sender),
canvas_layers_receiver: canvas_layers_receiver,
canvas_layers_sender: canvas_layers_sender,
rw_data: Arc::new(Mutex::new(
LayoutTaskData {
root_flow: None,
@ -375,6 +384,7 @@ impl LayoutTask {
constellation_chan: rw_data.constellation_chan.clone(),
layout_chan: self.chan.clone(),
font_cache_task: self.font_cache_task.clone(),
canvas_layers_sender: self.canvas_layers_sender.clone(),
stylist: &*rw_data.stylist,
url: (*url).clone(),
reflow_root: reflow_root.map(|node| OpaqueNodeMethods::from_layout_node(node)),
@ -960,6 +970,14 @@ impl LayoutTask {
animation::process_new_animations(&mut *rw_data, self.id);
}
// Send new canvas renderers to the paint task
while let Ok((layer_id, renderer)) = self.canvas_layers_receiver.try_recv() {
// Just send if there's an actual renderer
if let Some(renderer) = renderer {
self.paint_chan.send(PaintMsg::CanvasLayer(layer_id, renderer));
}
}
// Perform post-style recalculation layout passes.
self.perform_post_style_recalc_layout_passes(&data.reflow_info,
&mut rw_data,

View file

@ -39,13 +39,14 @@ extern crate util;
extern crate rustc_serialize;
extern crate alloc;
extern crate azure;
extern crate canvas;
extern crate canvas_traits;
extern crate clock_ticks;
extern crate collections;
extern crate cssparser;
extern crate encoding;
extern crate geom;
extern crate gfx;
extern crate gfx_traits;
extern crate layout_traits;
extern crate libc;
extern crate msg;

View file

@ -32,7 +32,7 @@
#![allow(unsafe_code)]
use canvas::canvas_msg::CanvasMsg;
use canvas_traits::CanvasMsg;
use context::SharedLayoutContext;
use css::node_style::StyledNode;
use incremental::RestyleDamage;