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.
This commit is contained in:
Patrick Walton 2016-03-09 18:25:12 -08:00
parent 9fcf9215d0
commit 0006c1923a
8 changed files with 151 additions and 70 deletions

View file

@ -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;
Ok(image) => return Some(image),
Err(ImageState::LoadError) => {
// Image failed to load, so just return nothing
return None
}
Err(_) => {}
}
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,
// 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,
}
}
}

View file

@ -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,
})

View file

@ -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(),
}
}

View file

@ -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,