Copy image-fetching code from Layout 2013

This commit is contained in:
Simon Sapin 2020-01-10 15:50:38 +01:00
parent cbcf83fc65
commit c8cbc57b76
6 changed files with 178 additions and 6 deletions

4
Cargo.lock generated
View file

@ -2744,12 +2744,14 @@ dependencies = [
"cssparser", "cssparser",
"embedder_traits", "embedder_traits",
"euclid", "euclid",
"fnv",
"gfx", "gfx",
"gfx_traits", "gfx_traits",
"ipc-channel", "ipc-channel",
"libc", "libc",
"msg", "msg",
"net_traits", "net_traits",
"parking_lot",
"range", "range",
"rayon", "rayon",
"rayon_croissant", "rayon_croissant",
@ -2758,6 +2760,7 @@ dependencies = [
"serde", "serde",
"servo_arc", "servo_arc",
"servo_geometry", "servo_geometry",
"servo_url",
"style", "style",
"style_traits", "style_traits",
"unicode-script", "unicode-script",
@ -2834,6 +2837,7 @@ dependencies = [
"metrics", "metrics",
"msg", "msg",
"net_traits", "net_traits",
"parking_lot",
"profile_traits", "profile_traits",
"range", "range",
"script", "script",

View file

@ -18,12 +18,14 @@ atomic_refcell = "0.1"
cssparser = "0.27" cssparser = "0.27"
embedder_traits = {path = "../embedder_traits"} embedder_traits = {path = "../embedder_traits"}
euclid = "0.20" euclid = "0.20"
fnv = "1.0"
gfx = {path = "../gfx"} gfx = {path = "../gfx"}
gfx_traits = {path = "../gfx_traits"} gfx_traits = {path = "../gfx_traits"}
ipc-channel = "0.12" ipc-channel = "0.12"
libc = "0.2" libc = "0.2"
msg = {path = "../msg"} msg = {path = "../msg"}
net_traits = {path = "../net_traits"} net_traits = {path = "../net_traits"}
parking_lot = "0.9"
range = {path = "../range"} range = {path = "../range"}
rayon = "1" rayon = "1"
rayon_croissant = "0.2.0" rayon_croissant = "0.2.0"
@ -32,6 +34,7 @@ script_traits = {path = "../script_traits"}
serde = "1.0" serde = "1.0"
servo_arc = { path = "../servo_arc" } servo_arc = { path = "../servo_arc" }
servo_geometry = {path = "../geometry"} servo_geometry = {path = "../geometry"}
servo_url = {path = "../url"}
style = {path = "../style", features = ["servo", "servo-layout-2020"]} style = {path = "../style", features = ["servo", "servo-layout-2020"]}
style_traits = {path = "../style_traits"} style_traits = {path = "../style_traits"}
unicode-script = {version = "0.3", features = ["harfbuzz"]} unicode-script = {version = "0.3", features = ["harfbuzz"]}

View file

@ -2,18 +2,51 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::display_list::WebRenderImageInfo;
use fnv::FnvHashMap;
use gfx::font_cache_thread::FontCacheThread; use gfx::font_cache_thread::FontCacheThread;
use gfx::font_context::FontContext; use gfx::font_context::FontContext;
use msg::constellation_msg::PipelineId; use msg::constellation_msg::PipelineId;
use net_traits::image_cache::{CanRequestImages, ImageCache, ImageState};
use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
use parking_lot::RwLock;
use script_layout_interface::{PendingImage, PendingImageState};
use servo_url::{ImmutableOrigin, ServoUrl};
use std::cell::RefCell; use std::cell::RefCell;
use std::sync::Mutex; use std::sync::{Arc, Mutex};
use style::context::SharedStyleContext; use style::context::SharedStyleContext;
use style::dom::OpaqueNode;
pub struct LayoutContext<'a> { pub struct LayoutContext<'a> {
pub id: PipelineId, pub id: PipelineId,
pub use_rayon: bool, pub use_rayon: bool,
pub origin: ImmutableOrigin,
/// Bits shared by the layout and style system.
pub style_context: SharedStyleContext<'a>, pub style_context: SharedStyleContext<'a>,
/// Interface to the font cache thread.
pub font_cache_thread: Mutex<FontCacheThread>, pub font_cache_thread: Mutex<FontCacheThread>,
/// Reference to the script thread image cache.
pub image_cache: Arc<dyn ImageCache>,
/// A list of in-progress image loads to be shared with the script thread.
/// A None value means that this layout was not initiated by the script thread.
pub pending_images: Option<Mutex<Vec<PendingImage>>>,
pub webrender_image_cache:
Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo>>>,
}
impl<'a> Drop for LayoutContext<'a> {
fn drop(&mut self) {
if !std::thread::panicking() {
if let Some(ref pending_images) = self.pending_images {
assert!(pending_images.lock().unwrap().is_empty());
}
}
}
} }
impl<'a> LayoutContext<'a> { impl<'a> LayoutContext<'a> {
@ -21,6 +54,100 @@ impl<'a> LayoutContext<'a> {
pub fn shared_context(&self) -> &SharedStyleContext { pub fn shared_context(&self) -> &SharedStyleContext {
&self.style_context &self.style_context
} }
pub fn get_or_request_image_or_meta(
&self,
node: OpaqueNode,
url: ServoUrl,
use_placeholder: UsePlaceholder,
) -> Option<ImageOrMetadataAvailable> {
//XXXjdm For cases where we do not request an image, we still need to
// ensure the node gets another script-initiated reflow or it
// won't be requested at all.
let can_request = if self.pending_images.is_some() {
CanRequestImages::Yes
} else {
CanRequestImages::No
};
// See if the image is already available
let result = self.image_cache.find_image_or_metadata(
url.clone(),
self.origin.clone(),
None,
use_placeholder,
can_request,
);
match result {
Ok(image_or_metadata) => Some(image_or_metadata),
// Image failed to load, so just return nothing
Err(ImageState::LoadError) => None,
// Not yet requested - request image or metadata from the cache
Err(ImageState::NotRequested(id)) => {
let image = PendingImage {
state: PendingImageState::Unrequested(url),
node: node.into(),
id: id,
};
self.pending_images
.as_ref()
.unwrap()
.lock()
.unwrap()
.push(image);
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.
Err(ImageState::Pending(id)) => {
//XXXjdm if self.pending_images is not available, we should make sure that
// this node gets marked dirty again so it gets a script-initiated
// reflow that deals with this properly.
if let Some(ref pending_images) = self.pending_images {
let image = PendingImage {
state: PendingImageState::PendingResponse,
node: node.into(),
id: id,
};
pending_images.lock().unwrap().push(image);
}
None
},
}
}
pub fn get_webrender_image_for_url(
&self,
node: OpaqueNode,
url: ServoUrl,
use_placeholder: UsePlaceholder,
) -> Option<WebRenderImageInfo> {
if let Some(existing_webrender_image) = self
.webrender_image_cache
.read()
.get(&(url.clone(), use_placeholder))
{
return Some((*existing_webrender_image).clone());
}
match self.get_or_request_image_or_meta(node, url.clone(), use_placeholder) {
Some(ImageOrMetadataAvailable::ImageAvailable(image, _)) => {
let image_info = WebRenderImageInfo {
width: image.width,
height: image.height,
key: image.id,
};
if image_info.key.is_none() {
Some(image_info)
} else {
let mut webrender_image_cache = self.webrender_image_cache.write();
webrender_image_cache.insert((url, use_placeholder), image_info);
Some(image_info)
}
},
None | Some(ImageOrMetadataAvailable::MetadataAvailable(_)) => None,
}
}
} }
pub(crate) type LayoutFontContext = FontContext<FontCacheThread>; pub(crate) type LayoutFontContext = FontContext<FontCacheThread>;

View file

@ -14,6 +14,13 @@ use style::values::computed::{BorderStyle, Length, LengthPercentage};
use style::values::specified::ui::CursorKind; use style::values::specified::ui::CursorKind;
use webrender_api::{self as wr, units}; use webrender_api::{self as wr, units};
#[derive(Clone, Copy)]
pub struct WebRenderImageInfo {
pub width: u32,
pub height: u32,
pub key: Option<wr::ImageKey>,
}
// `webrender_api::display_item::ItemTag` is private // `webrender_api::display_item::ItemTag` is private
type ItemTag = (u64, u16); type ItemTag = (u64, u16);
type HitInfo = Option<ItemTag>; type HitInfo = Option<ItemTag>;

View file

@ -32,6 +32,7 @@ malloc_size_of = { path = "../malloc_size_of" }
metrics = {path = "../metrics"} metrics = {path = "../metrics"}
msg = {path = "../msg"} msg = {path = "../msg"}
net_traits = {path = "../net_traits"} net_traits = {path = "../net_traits"}
parking_lot = { version = "0.9", features = ["nightly"] }
profile_traits = {path = "../profile_traits"} profile_traits = {path = "../profile_traits"}
range = {path = "../range"} range = {path = "../range"}
script = {path = "../script"} script = {path = "../script"}

View file

@ -35,7 +35,7 @@ use gfx_traits::{node_id_from_scroll_id, Epoch};
use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER; use ipc_channel::router::ROUTER;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list::DisplayListBuilder; use layout::display_list::{DisplayListBuilder, WebRenderImageInfo};
use layout::query::{ use layout::query::{
process_content_box_request, process_content_boxes_request, LayoutRPCImpl, LayoutThreadData, process_content_box_request, process_content_boxes_request, LayoutRPCImpl, LayoutThreadData,
}; };
@ -56,7 +56,8 @@ use msg::constellation_msg::{
}; };
use msg::constellation_msg::{LayoutHangAnnotation, MonitoredComponentType, PipelineId}; use msg::constellation_msg::{LayoutHangAnnotation, MonitoredComponentType, PipelineId};
use msg::constellation_msg::{MonitoredComponentId, TopLevelBrowsingContextId}; use msg::constellation_msg::{MonitoredComponentId, TopLevelBrowsingContextId};
use net_traits::image_cache::ImageCache; use net_traits::image_cache::{ImageCache, UsePlaceholder};
use parking_lot::RwLock;
use profile_traits::mem::{self as profile_mem, Report, ReportKind, ReportsChan}; use profile_traits::mem::{self as profile_mem, Report, ReportKind, ReportsChan};
use profile_traits::time::{self as profile_time, profile, TimerMetadata}; use profile_traits::time::{self as profile_time, profile, TimerMetadata};
use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType}; use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType};
@ -147,6 +148,9 @@ pub struct LayoutThread {
/// The channel on which messages can be sent to the memory profiler. /// The channel on which messages can be sent to the memory profiler.
mem_profiler_chan: profile_mem::ProfilerChan, mem_profiler_chan: profile_mem::ProfilerChan,
/// Reference to the script thread image cache.
image_cache: Arc<dyn ImageCache>,
/// Public interface to the font cache thread. /// Public interface to the font cache thread.
font_cache_thread: FontCacheThread, font_cache_thread: FontCacheThread,
@ -189,6 +193,8 @@ pub struct LayoutThread {
/// All the other elements of this struct are read-only. /// All the other elements of this struct are read-only.
rw_data: Arc<Mutex<LayoutThreadData>>, rw_data: Arc<Mutex<LayoutThreadData>>,
webrender_image_cache: Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo>>>,
/// The executors for paint worklets. /// The executors for paint worklets.
registered_painters: RegisteredPaintersImpl, registered_painters: RegisteredPaintersImpl,
@ -232,7 +238,7 @@ impl LayoutThreadFactory for LayoutThread {
background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>, background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>,
constellation_chan: IpcSender<ConstellationMsg>, constellation_chan: IpcSender<ConstellationMsg>,
script_chan: IpcSender<ConstellationControlMsg>, script_chan: IpcSender<ConstellationControlMsg>,
_image_cache: Arc<dyn ImageCache>, image_cache: Arc<dyn ImageCache>,
font_cache_thread: FontCacheThread, font_cache_thread: FontCacheThread,
time_profiler_chan: profile_time::ProfilerChan, time_profiler_chan: profile_time::ProfilerChan,
mem_profiler_chan: profile_mem::ProfilerChan, mem_profiler_chan: profile_mem::ProfilerChan,
@ -280,6 +286,7 @@ impl LayoutThreadFactory for LayoutThread {
background_hang_monitor, background_hang_monitor,
constellation_chan, constellation_chan,
script_chan, script_chan,
image_cache,
font_cache_thread, font_cache_thread,
time_profiler_chan, time_profiler_chan,
mem_profiler_chan.clone(), mem_profiler_chan.clone(),
@ -443,6 +450,7 @@ impl LayoutThread {
background_hang_monitor: Box<dyn BackgroundHangMonitor>, background_hang_monitor: Box<dyn BackgroundHangMonitor>,
constellation_chan: IpcSender<ConstellationMsg>, constellation_chan: IpcSender<ConstellationMsg>,
script_chan: IpcSender<ConstellationControlMsg>, script_chan: IpcSender<ConstellationControlMsg>,
image_cache: Arc<dyn ImageCache>,
font_cache_thread: FontCacheThread, font_cache_thread: FontCacheThread,
time_profiler_chan: profile_time::ProfilerChan, time_profiler_chan: profile_time::ProfilerChan,
mem_profiler_chan: profile_mem::ProfilerChan, mem_profiler_chan: profile_mem::ProfilerChan,
@ -489,6 +497,7 @@ impl LayoutThread {
time_profiler_chan: time_profiler_chan, time_profiler_chan: time_profiler_chan,
mem_profiler_chan: mem_profiler_chan, mem_profiler_chan: mem_profiler_chan,
registered_painters: RegisteredPaintersImpl(Default::default()), registered_painters: RegisteredPaintersImpl(Default::default()),
image_cache,
font_cache_thread: font_cache_thread, font_cache_thread: font_cache_thread,
first_reflow: Cell::new(true), first_reflow: Cell::new(true),
font_cache_receiver: font_cache_receiver, font_cache_receiver: font_cache_receiver,
@ -523,6 +532,7 @@ impl LayoutThread {
element_inner_text_response: String::new(), element_inner_text_response: String::new(),
inner_window_dimensions_response: None, inner_window_dimensions_response: None,
})), })),
webrender_image_cache: Default::default(),
timer: if pref!(layout.animations.test.enabled) { timer: if pref!(layout.animations.test.enabled) {
Timer::test_mode() Timer::test_mode()
} else { } else {
@ -553,6 +563,7 @@ impl LayoutThread {
fn build_layout_context<'a>( fn build_layout_context<'a>(
&'a self, &'a self,
guards: StylesheetGuards<'a>, guards: StylesheetGuards<'a>,
script_initiated_layout: bool,
snapshot_map: &'a SnapshotMap, snapshot_map: &'a SnapshotMap,
) -> LayoutContext<'a> { ) -> LayoutContext<'a> {
let thread_local_style_context_creation_data = let thread_local_style_context_creation_data =
@ -560,6 +571,7 @@ impl LayoutThread {
LayoutContext { LayoutContext {
id: self.id, id: self.id,
origin: self.url.origin(),
style_context: SharedStyleContext { style_context: SharedStyleContext {
stylist: &self.stylist, stylist: &self.stylist,
options: GLOBAL_STYLE_DATA.options.clone(), options: GLOBAL_STYLE_DATA.options.clone(),
@ -573,7 +585,14 @@ impl LayoutThread {
traversal_flags: TraversalFlags::empty(), traversal_flags: TraversalFlags::empty(),
snapshot_map: snapshot_map, snapshot_map: snapshot_map,
}, },
image_cache: self.image_cache.clone(),
font_cache_thread: Mutex::new(self.font_cache_thread.clone()), font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
webrender_image_cache: self.webrender_image_cache.clone(),
pending_images: if script_initiated_layout {
Some(Mutex::new(Vec::new()))
} else {
None
},
use_rayon: STYLE_THREAD_POOL.pool().is_some(), use_rayon: STYLE_THREAD_POOL.pool().is_some(),
} }
} }
@ -1074,7 +1093,7 @@ impl LayoutThread {
self.stylist.flush(&guards, Some(element), Some(&map)); self.stylist.flush(&guards, Some(element), Some(&map));
// Create a layout context for use throughout the following passes. // Create a layout context for use throughout the following passes.
let mut layout_context = self.build_layout_context(guards.clone(), &map); let mut layout_context = self.build_layout_context(guards.clone(), true, &map);
let traversal = RecalcStyle::new(layout_context); let traversal = RecalcStyle::new(layout_context);
let token = { let token = {
@ -1132,7 +1151,12 @@ impl LayoutThread {
} }
self.first_reflow.set(false); self.first_reflow.set(false);
self.respond_to_query_if_necessary(&data.reflow_goal, &mut *rw_data, &mut layout_context); self.respond_to_query_if_necessary(
&data.reflow_goal,
&mut *rw_data,
&mut layout_context,
data.result.borrow_mut().as_mut().unwrap(),
);
} }
fn respond_to_query_if_necessary( fn respond_to_query_if_necessary(
@ -1140,7 +1164,13 @@ impl LayoutThread {
reflow_goal: &ReflowGoal, reflow_goal: &ReflowGoal,
rw_data: &mut LayoutThreadData, rw_data: &mut LayoutThreadData,
context: &mut LayoutContext, context: &mut LayoutContext,
reflow_result: &mut ReflowComplete,
) { ) {
let pending_images = match &context.pending_images {
Some(pending) => std::mem::take(&mut *pending.lock().unwrap()),
None => Vec::new(),
};
reflow_result.pending_images = pending_images;
match *reflow_goal { match *reflow_goal {
ReflowGoal::LayoutQuery(ref querymsg, _) => match querymsg { ReflowGoal::LayoutQuery(ref querymsg, _) => match querymsg {
&QueryMsg::ContentBoxQuery(node) => { &QueryMsg::ContentBoxQuery(node) => {