aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2016-03-09 18:25:12 -0800
committerPatrick Walton <pcwalton@mimiga.net>2016-03-22 14:49:47 -0700
commit0006c1923a87d7de6b568a645fb72be16900fabc (patch)
tree1ef8c26c54b2b39bce49ce881ae2f073cd0c3cb1
parent9fcf9215d008c4d29482f866812866f7169e6d15 (diff)
downloadservo-0006c1923a87d7de6b568a645fb72be16900fabc.tar.gz
servo-0006c1923a87d7de6b568a645fb72be16900fabc.zip
gfx: Allow images to be shipped to the WebRender thread without shipping
over the data as well. WebRender doesn't need the data, as it acquires it separately. About a 50%-100% improvement in display list building time on browser.html.
-rw-r--r--components/gfx/display_list/mod.rs44
-rw-r--r--components/gfx/paint_context.rs19
-rw-r--r--components/layout/context.rs110
-rw-r--r--components/layout/display_list_builder.rs33
-rw-r--r--components/layout/layout_thread.rs11
-rw-r--r--components/layout/webrender_helpers.rs2
-rw-r--r--components/msg/constellation_msg.rs2
-rw-r--r--components/net_traits/image_cache_thread.rs2
8 files changed, 152 insertions, 71 deletions
diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs
index 97e2260ba99..7bba2ce0e53 100644
--- a/components/gfx/display_list/mod.rs
+++ b/components/gfx/display_list/mod.rs
@@ -24,8 +24,9 @@ use euclid::{Matrix2D, Matrix4, Point2D, Rect, SideOffsets2D, Size2D};
use fnv::FnvHasher;
use gfx_traits::{LayerId, ScrollPolicy};
use heapsize::HeapSizeOf;
+use ipc_channel::ipc::IpcSharedMemory;
use msg::constellation_msg::PipelineId;
-use net_traits::image::base::Image;
+use net_traits::image::base::{Image, PixelFormat};
use paint_context::PaintContext;
use range::Range;
use serde::de::{self, Deserialize, Deserializer, MapVisitor, Visitor};
@@ -46,7 +47,7 @@ use text::TextRun;
use text::glyph::CharIndex;
use util::geometry::{self, MAX_RECT, ScreenPx};
use util::print_tree::PrintTree;
-use webrender_traits::WebGLContextId;
+use webrender_traits::{self, WebGLContextId};
pub use style::dom::OpaqueNode;
@@ -986,8 +987,11 @@ pub enum TextOrientation {
#[derive(Clone, HeapSizeOf, Deserialize, Serialize)]
pub struct ImageDisplayItem {
pub base: BaseDisplayItem,
+
+ pub webrender_image: WebRenderImageInfo,
+
#[ignore_heap_size_of = "Because it is non-owning"]
- pub image: Arc<Image>,
+ pub image_data: Option<Arc<IpcSharedMemory>>,
/// The dimensions to which the image display item should be stretched. If this is smaller than
/// the bounds of this display item, then the image will be repeated in the appropriate
@@ -1203,10 +1207,14 @@ impl DisplayItem {
DisplayItem::ImageClass(ref image_item) => {
debug!("Drawing image at {:?}.", image_item.base.bounds);
- paint_context.draw_image(&image_item.base.bounds,
- &image_item.stretch_size,
- image_item.image.clone(),
- image_item.image_rendering.clone());
+ paint_context.draw_image(
+ &image_item.base.bounds,
+ &image_item.stretch_size,
+ &image_item.webrender_image,
+ &image_item.image_data
+ .as_ref()
+ .expect("Non-WR painting needs image data!")[..],
+ image_item.image_rendering.clone());
}
DisplayItem::WebGLClass(_) => {
@@ -1390,3 +1398,25 @@ impl StackingContextId {
StackingContextId(fragment_type, id)
}
}
+
+#[derive(Copy, Clone, HeapSizeOf, Deserialize, Serialize)]
+pub struct WebRenderImageInfo {
+ pub width: u32,
+ pub height: u32,
+ pub format: PixelFormat,
+ #[ignore_heap_size_of = "WebRender traits type, and tiny"]
+ pub key: Option<webrender_traits::ImageKey>,
+}
+
+impl WebRenderImageInfo {
+ #[inline]
+ pub fn from_image(image: &Image) -> WebRenderImageInfo {
+ WebRenderImageInfo {
+ width: image.width,
+ height: image.height,
+ format: image.format,
+ key: image.id,
+ }
+ }
+}
+
diff --git a/components/gfx/paint_context.rs b/components/gfx/paint_context.rs
index 9fded4e8651..7b8813d907e 100644
--- a/components/gfx/paint_context.rs
+++ b/components/gfx/paint_context.rs
@@ -17,7 +17,7 @@ use azure::{AzDrawTargetFillGlyphs, struct__AzGlyphBuffer, struct__AzPoint};
use azure::{AzFloat, struct__AzDrawOptions, struct__AzGlyph};
use display_list::TextOrientation::{SidewaysLeft, SidewaysRight, Upright};
use display_list::{BLUR_INFLATION_FACTOR, BorderRadii, BoxShadowClipMode, ClippingRegion};
-use display_list::{TextDisplayItem};
+use display_list::{TextDisplayItem, WebRenderImageInfo};
use euclid::matrix2d::Matrix2D;
use euclid::point::Point2D;
use euclid::rect::{Rect, TypedRect};
@@ -27,10 +27,9 @@ use euclid::size::Size2D;
use filters;
use font_context::FontContext;
use gfx_traits::{color, LayerKind};
-use net_traits::image::base::{Image, PixelFormat};
+use net_traits::image::base::PixelFormat;
use range::Range;
use std::default::Default;
-use std::sync::Arc;
use std::{f32, mem, ptr};
use style::computed_values::{border_style, filter, image_rendering, mix_blend_mode};
use text::TextRun;
@@ -181,21 +180,22 @@ impl<'a> PaintContext<'a> {
pub fn draw_image(&self,
bounds: &Rect<Au>,
stretch_size: &Size2D<Au>,
- image: Arc<Image>,
+ image_info: &WebRenderImageInfo,
+ image_data: &[u8],
image_rendering: image_rendering::T) {
- let size = Size2D::new(image.width as i32, image.height as i32);
- let (pixel_width, source_format) = match image.format {
+ let size = Size2D::new(image_info.width as i32, image_info.height as i32);
+ let (pixel_width, source_format) = match image_info.format {
PixelFormat::RGBA8 => (4, SurfaceFormat::B8G8R8A8),
PixelFormat::K8 => (1, SurfaceFormat::A8),
PixelFormat::RGB8 => panic!("RGB8 color type not supported"),
PixelFormat::KA8 => panic!("KA8 color type not supported"),
};
- let stride = image.width * pixel_width;
+ let stride = image_info.width * pixel_width;
let scale = self.screen_pixels_per_px();
self.draw_target.make_current();
let draw_target_ref = &self.draw_target;
- let azure_surface = match draw_target_ref.create_source_surface_from_data(&image.bytes,
+ let azure_surface = match draw_target_ref.create_source_surface_from_data(image_data,
size,
stride as i32,
source_format) {
@@ -204,7 +204,8 @@ impl<'a> PaintContext<'a> {
};
let source_rect = Rect::new(Point2D::new(0.0, 0.0),
- Size2D::new(image.width as AzFloat, image.height as AzFloat));
+ Size2D::new(image_info.width as AzFloat,
+ image_info.height as AzFloat));
let dest_rect = bounds.to_nearest_azure_rect(scale);
// TODO(pcwalton): According to CSS-IMAGES-3 § 5.3, nearest-neighbor interpolation is a
diff --git a/components/layout/context.rs b/components/layout/context.rs
index 49dc10cb457..143e78f810e 100644
--- a/components/layout/context.rs
+++ b/components/layout/context.rs
@@ -11,11 +11,12 @@ use app_units::Au;
use canvas_traits::CanvasMsg;
use euclid::Rect;
use fnv::FnvHasher;
+use gfx::display_list::WebRenderImageInfo;
use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context::FontContext;
use gfx_traits::LayerId;
use heapsize::HeapSizeOf;
-use ipc_channel::ipc::{self, IpcSender};
+use ipc_channel::ipc::{self, IpcSender, IpcSharedMemory};
use net_traits::image::base::Image;
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread, ImageResponse, ImageState};
use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder};
@@ -24,7 +25,7 @@ use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use std::rc::Rc;
use std::sync::mpsc::Sender;
-use std::sync::{Arc, Mutex};
+use std::sync::{Arc, Mutex, RwLock};
use style::context::{LocalStyleContext, StyleContext};
use style::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
use style::selector_impl::ServoSelectorImpl;
@@ -99,6 +100,11 @@ pub struct SharedLayoutContext {
/// The visible rects for each layer, as reported to us by the compositor.
pub visible_rects: Arc<HashMap<LayerId, Rect<Au>, BuildHasherDefault<FnvHasher>>>,
+
+ /// A cache of WebRender image info.
+ pub webrender_image_cache: Arc<RwLock<HashMap<(Url, UsePlaceholder),
+ WebRenderImageInfo,
+ BuildHasherDefault<FnvHasher>>>>,
}
pub struct LayoutContext<'a> {
@@ -132,45 +138,36 @@ impl<'a> LayoutContext<'a> {
self.cached_local_layout_context.font_context.borrow_mut()
}
- pub fn get_or_request_image(&self, url: Url, use_placeholder: UsePlaceholder)
- -> Option<Arc<Image>> {
+ fn get_or_request_image_synchronously(&self, url: Url, use_placeholder: UsePlaceholder)
+ -> Option<Arc<Image>> {
+ debug_assert!(opts::get().output_file.is_some() || opts::get().exit_after_load);
+
// See if the image is already available
- let result = self.shared.image_cache_thread.find_image(url.clone(),
- use_placeholder);
+ let result = self.shared.image_cache_thread.find_image(url.clone(), use_placeholder);
match result {
- Ok(image) => Some(image),
- Err(state) => {
- // If we are emitting an output file, then we need to block on
- // image load or we risk emitting an output file missing the image.
- let is_sync = opts::get().output_file.is_some() ||
- opts::get().exit_after_load;
-
- match (state, is_sync) {
- // Image failed to load, so just return nothing
- (ImageState::LoadError, _) => None,
- // Not loaded, test mode - load the image synchronously
- (_, true) => {
- let (sync_tx, sync_rx) = ipc::channel().unwrap();
- self.shared.image_cache_thread.request_image(url,
- ImageCacheChan(sync_tx),
- None);
- match sync_rx.recv().unwrap().image_response {
- ImageResponse::Loaded(image) |
- ImageResponse::PlaceholderLoaded(image) => Some(image),
- ImageResponse::None | ImageResponse::MetadataLoaded(_) => None,
+ Ok(image) => return Some(image),
+ Err(ImageState::LoadError) => {
+ // Image failed to load, so just return nothing
+ return None
+ }
+ Err(_) => {}
+ }
+
+ // If we are emitting an output file, then we need to block on
+ // image load or we risk emitting an output file missing the image.
+ let (sync_tx, sync_rx) = ipc::channel().unwrap();
+ self.shared.image_cache_thread.request_image(url, ImageCacheChan(sync_tx), None);
+ loop {
+ match sync_rx.recv() {
+ Err(_) => return None,
+ Ok(response) => {
+ match response.image_response {
+ ImageResponse::Loaded(image) | ImageResponse::PlaceholderLoaded(image) => {
+ return Some(image)
}
+ ImageResponse::None | ImageResponse::MetadataLoaded(_) => {}
}
- // Not yet requested, async mode - request image from the cache
- (ImageState::NotRequested, false) => {
- let sender = self.shared.image_cache_sender.lock().unwrap().clone();
- self.shared.image_cache_thread.request_image(url, sender, None);
- None
- }
- // Image has been requested, is still pending. Return no image
- // for this paint loop. When the image loads it will trigger
- // a reflow and/or repaint.
- (ImageState::Pending, false) => None,
}
}
}
@@ -180,7 +177,7 @@ impl<'a> LayoutContext<'a> {
-> Option<ImageOrMetadataAvailable> {
// If we are emitting an output file, load the image synchronously.
if opts::get().output_file.is_some() || opts::get().exit_after_load {
- return self.get_or_request_image(url, use_placeholder)
+ return self.get_or_request_image_synchronously(url, use_placeholder)
.map(|img| ImageOrMetadataAvailable::ImageAvailable(img));
}
// See if the image is already available
@@ -202,4 +199,43 @@ impl<'a> LayoutContext<'a> {
}
}
+ pub fn get_webrender_image_for_url(&self,
+ url: &Url,
+ use_placeholder: UsePlaceholder,
+ fetch_image_data_as_well: bool)
+ -> Option<(WebRenderImageInfo, Option<IpcSharedMemory>)> {
+ if !fetch_image_data_as_well {
+ let webrender_image_cache = self.shared.webrender_image_cache.read().unwrap();
+ if let Some(existing_webrender_image) =
+ webrender_image_cache.get(&((*url).clone(), use_placeholder)) {
+ return Some(((*existing_webrender_image).clone(), None))
+ }
+ }
+
+ match self.get_or_request_image_or_meta((*url).clone(), use_placeholder) {
+ Some(ImageOrMetadataAvailable::ImageAvailable(image)) => {
+ let image_info = WebRenderImageInfo::from_image(&*image);
+ if image_info.key.is_none() {
+ let bytes = if !fetch_image_data_as_well {
+ None
+ } else {
+ Some(image.bytes.clone())
+ };
+ Some((image_info, bytes))
+ } else if !fetch_image_data_as_well {
+ let mut webrender_image_cache = self.shared
+ .webrender_image_cache
+ .write()
+ .unwrap();
+ webrender_image_cache.insert(((*url).clone(), use_placeholder),
+ image_info);
+ Some((image_info, None))
+ } else {
+ Some((image_info, Some(image.bytes.clone())))
+ }
+ }
+ None | Some(ImageOrMetadataAvailable::MetadataAvailable(_)) => None,
+ }
+ }
}
+
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs
index f498d83c389..407d4d19a05 100644
--- a/components/layout/display_list_builder.rs
+++ b/components/layout/display_list_builder.rs
@@ -29,7 +29,7 @@ use gfx::display_list::{GradientDisplayItem};
use gfx::display_list::{GradientStop, IframeDisplayItem, ImageDisplayItem, WebGLDisplayItem, LayeredItem, LayerInfo};
use gfx::display_list::{LineDisplayItem, OpaqueNode, SolidColorDisplayItem};
use gfx::display_list::{StackingContext, StackingContextId, StackingContextType};
-use gfx::display_list::{TextDisplayItem, TextOrientation, DisplayListEntry};
+use gfx::display_list::{TextDisplayItem, TextOrientation, DisplayListEntry, WebRenderImageInfo};
use gfx::paint_thread::THREAD_TINT_COLORS;
use gfx::text::glyph::CharIndex;
use gfx_traits::{color, ScrollPolicy};
@@ -37,7 +37,7 @@ use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, LAST_FRAGMENT_OF_ELEMENT};
use ipc_channel::ipc::{self, IpcSharedMemory};
use list_item::ListItemFlow;
use model::{self, MaybeAuto, ToGfxMatrix};
-use net_traits::image::base::{Image, PixelFormat};
+use net_traits::image::base::PixelFormat;
use net_traits::image_cache_thread::UsePlaceholder;
use range::Range;
use std::default::Default;
@@ -136,7 +136,7 @@ pub trait FragmentDisplayListBuilding {
fn compute_background_image_size(&self,
style: &ComputedValues,
bounds: &Rect<Au>,
- image: &Image)
+ image: &WebRenderImageInfo)
-> Size2D<Au>;
/// Adds the display items necessary to paint the background image of this fragment to the
@@ -404,7 +404,7 @@ impl FragmentDisplayListBuilding for Fragment {
fn compute_background_image_size(&self,
style: &ComputedValues,
bounds: &Rect<Au>,
- image: &Image)
+ image: &WebRenderImageInfo)
-> Size2D<Au> {
// If `image_aspect_ratio` < `bounds_aspect_ratio`, the image is tall; otherwise, it is
// wide.
@@ -462,14 +462,17 @@ impl FragmentDisplayListBuilding for Fragment {
clip: &ClippingRegion,
image_url: &Url) {
let background = style.get_background();
- let image =
- state.layout_context.get_or_request_image(image_url.clone(), UsePlaceholder::No);
- if let Some(image) = image {
+ let fetch_image_data_as_well = !opts::get().use_webrender;
+ let webrender_image =
+ state.layout_context.get_webrender_image_for_url(image_url,
+ UsePlaceholder::No,
+ fetch_image_data_as_well);
+ if let Some((webrender_image, image_data)) = webrender_image {
debug!("(building display list) building background image");
// Use `background-size` to get the size.
let mut bounds = *absolute_bounds;
- let image_size = self.compute_background_image_size(style, &bounds, &*image);
+ let image_size = self.compute_background_image_size(style, &bounds, &webrender_image);
// Clip.
//
@@ -560,7 +563,8 @@ impl FragmentDisplayListBuilding for Fragment {
style,
Cursor::DefaultCursor),
&clip),
- image: image.clone(),
+ webrender_image: webrender_image,
+ image_data: image_data.map(Arc::new),
stretch_size: Size2D::new(image_size.width, image_size.height),
image_rendering: style.get_effects().image_rendering.clone(),
}), display_list_section);
@@ -1173,7 +1177,8 @@ impl FragmentDisplayListBuilding for Fragment {
&*self.style,
Cursor::DefaultCursor),
clip),
- image: image.clone(),
+ webrender_image: WebRenderImageInfo::from_image(image),
+ image_data: Some(Arc::new(image.bytes.clone())),
stretch_size: stacking_relative_content_box.size,
image_rendering: self.style.get_effects().image_rendering.clone(),
}), DisplayListSection::Content);
@@ -1214,13 +1219,13 @@ impl FragmentDisplayListBuilding for Fragment {
&*self.style,
Cursor::DefaultCursor),
clip),
- image: Arc::new(Image {
+ image_data: Some(Arc::new(canvas_data.image_data)),
+ webrender_image: WebRenderImageInfo {
width: width as u32,
height: height as u32,
format: PixelFormat::RGBA8,
- bytes: canvas_data.image_data,
- id: canvas_data.image_key,
- }),
+ key: canvas_data.image_key,
+ },
stretch_size: stacking_relative_content_box.size,
image_rendering: image_rendering::T::Auto,
})
diff --git a/components/layout/layout_thread.rs b/components/layout/layout_thread.rs
index 603fa35472c..041eab87168 100644
--- a/components/layout/layout_thread.rs
+++ b/components/layout/layout_thread.rs
@@ -24,6 +24,7 @@ use flow_ref::{self, FlowRef};
use fnv::FnvHasher;
use gfx::display_list::{ClippingRegion, DisplayItemMetadata, DisplayList, LayerInfo};
use gfx::display_list::{OpaqueNode, StackingContext, StackingContextId, StackingContextType};
+use gfx::display_list::{WebRenderImageInfo};
use gfx::font;
use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context;
@@ -39,6 +40,7 @@ use layout_traits::LayoutThreadFactory;
use log;
use msg::constellation_msg::{ConstellationChan, ConvertPipelineIdToWebRender, Failure, PipelineId};
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread};
+use net_traits::image_cache_thread::{UsePlaceholder};
use parallel;
use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType};
@@ -84,7 +86,7 @@ use util::thread;
use util::thread_state;
use util::workqueue::WorkQueue;
use webrender_helpers::{WebRenderDisplayListConverter, WebRenderFrameBuilder};
-use webrender_traits::{self, AuxiliaryListsBuilder};
+use webrender_traits;
use wrapper::{LayoutNode, NonOpaqueStyleAndLayoutData, ServoLayoutNode, ThreadSafeLayoutNode};
/// The number of screens of data we're allowed to generate display lists for in each direction.
@@ -235,6 +237,10 @@ pub struct LayoutThread {
/// The CSS error reporter for all CSS loaded in this layout thread
error_reporter: CSSErrorReporter,
+ webrender_image_cache: Arc<RwLock<HashMap<(Url, UsePlaceholder),
+ WebRenderImageInfo,
+ BuildHasherDefault<FnvHasher>>>>,
+
// Webrender interface, if enabled.
webrender_api: Option<webrender_traits::RenderApi>,
}
@@ -475,6 +481,8 @@ impl LayoutThread {
pipelineid: id,
script_chan: Arc::new(Mutex::new(script_chan)),
},
+ webrender_image_cache:
+ Arc::new(RwLock::new(HashMap::with_hasher(Default::default()))),
}
}
@@ -516,6 +524,7 @@ impl LayoutThread {
canvas_layers_sender: Mutex::new(self.canvas_layers_sender.clone()),
url: (*url).clone(),
visible_rects: self.visible_rects.clone(),
+ webrender_image_cache: self.webrender_image_cache.clone(),
}
}
diff --git a/components/layout/webrender_helpers.rs b/components/layout/webrender_helpers.rs
index 1f5f9671821..fcf0e204a0d 100644
--- a/components/layout/webrender_helpers.rs
+++ b/components/layout/webrender_helpers.rs
@@ -436,7 +436,7 @@ impl WebRenderDisplayItemConverter for DisplayItem {
}
}
DisplayItem::ImageClass(ref item) => {
- if let Some(id) = item.image.id {
+ if let Some(id) = item.webrender_image.key {
if item.stretch_size.width > Au(0) &&
item.stretch_size.height > Au(0) {
builder.push_image(level,
diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs
index 83f308b26fd..34512fedf8b 100644
--- a/components/msg/constellation_msg.rs
+++ b/components/msg/constellation_msg.rs
@@ -222,7 +222,7 @@ pub struct ImageMetadata {
pub height: u32,
}
-#[derive(Deserialize, Serialize, HeapSizeOf)]
+#[derive(Clone, Deserialize, Serialize, HeapSizeOf)]
pub struct Image {
pub width: u32,
pub height: u32,
diff --git a/components/net_traits/image_cache_thread.rs b/components/net_traits/image_cache_thread.rs
index 8324bae8b23..0011cab8e9f 100644
--- a/components/net_traits/image_cache_thread.rs
+++ b/components/net_traits/image_cache_thread.rs
@@ -96,7 +96,7 @@ pub enum ImageCacheCommand {
Exit(IpcSender<()>),
}
-#[derive(Copy, Clone, PartialEq, Deserialize, Serialize)]
+#[derive(Copy, Clone, PartialEq, Hash, Eq, Deserialize, Serialize)]
pub enum UsePlaceholder {
No,
Yes,