diff options
author | Patrick Walton <pcwalton@mimiga.net> | 2016-03-09 18:25:12 -0800 |
---|---|---|
committer | Patrick Walton <pcwalton@mimiga.net> | 2016-03-22 14:49:47 -0700 |
commit | 0006c1923a87d7de6b568a645fb72be16900fabc (patch) | |
tree | 1ef8c26c54b2b39bce49ce881ae2f073cd0c3cb1 | |
parent | 9fcf9215d008c4d29482f866812866f7169e6d15 (diff) | |
download | servo-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.rs | 44 | ||||
-rw-r--r-- | components/gfx/paint_context.rs | 19 | ||||
-rw-r--r-- | components/layout/context.rs | 110 | ||||
-rw-r--r-- | components/layout/display_list_builder.rs | 33 | ||||
-rw-r--r-- | components/layout/layout_thread.rs | 11 | ||||
-rw-r--r-- | components/layout/webrender_helpers.rs | 2 | ||||
-rw-r--r-- | components/msg/constellation_msg.rs | 2 | ||||
-rw-r--r-- | components/net_traits/image_cache_thread.rs | 2 |
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, |