mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Synchronously decode images if decoded bytes are requested after the full response is received.
This commit is contained in:
parent
e0837134ff
commit
21118f0a8a
1 changed files with 107 additions and 55 deletions
|
@ -57,7 +57,7 @@ fn is_image_opaque(format: webrender_traits::ImageFormat, bytes: &[u8]) -> bool
|
||||||
struct PendingLoad {
|
struct PendingLoad {
|
||||||
// The bytes loaded so far. Reset to an empty vector once loading
|
// The bytes loaded so far. Reset to an empty vector once loading
|
||||||
// is complete and the buffer has been transmitted to the decoder.
|
// is complete and the buffer has been transmitted to the decoder.
|
||||||
bytes: Vec<u8>,
|
bytes: ImageBytes,
|
||||||
|
|
||||||
// Image metadata, if available.
|
// Image metadata, if available.
|
||||||
metadata: Option<ImageMetadata>,
|
metadata: Option<ImageMetadata>,
|
||||||
|
@ -71,6 +71,40 @@ struct PendingLoad {
|
||||||
url: ServoUrl,
|
url: ServoUrl,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ImageBytes {
|
||||||
|
InProgress(Vec<u8>),
|
||||||
|
Complete(Arc<Vec<u8>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageBytes {
|
||||||
|
fn extend_from_slice(&mut self, data: &[u8]) {
|
||||||
|
match *self {
|
||||||
|
ImageBytes::InProgress(ref mut bytes) => bytes.extend_from_slice(data),
|
||||||
|
ImageBytes::Complete(_) => panic!("attempted modification of complete image bytes"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mark_complete(&mut self) -> Arc<Vec<u8>> {
|
||||||
|
let bytes = {
|
||||||
|
let own_bytes = match *self {
|
||||||
|
ImageBytes::InProgress(ref mut bytes) => bytes,
|
||||||
|
ImageBytes::Complete(_) => panic!("attempted modification of complete image bytes"),
|
||||||
|
};
|
||||||
|
mem::replace(own_bytes, vec![])
|
||||||
|
};
|
||||||
|
let bytes = Arc::new(bytes);
|
||||||
|
*self = ImageBytes::Complete(bytes.clone());
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_slice(&self) -> &[u8] {
|
||||||
|
match *self {
|
||||||
|
ImageBytes::InProgress(ref bytes) => &bytes,
|
||||||
|
ImageBytes::Complete(ref bytes) => &*bytes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum LoadResult {
|
enum LoadResult {
|
||||||
Loaded(Image),
|
Loaded(Image),
|
||||||
PlaceholderLoaded(Arc<Image>),
|
PlaceholderLoaded(Arc<Image>),
|
||||||
|
@ -80,7 +114,7 @@ enum LoadResult {
|
||||||
impl PendingLoad {
|
impl PendingLoad {
|
||||||
fn new(url: ServoUrl) -> PendingLoad {
|
fn new(url: ServoUrl) -> PendingLoad {
|
||||||
PendingLoad {
|
PendingLoad {
|
||||||
bytes: vec!(),
|
bytes: ImageBytes::InProgress(vec!()),
|
||||||
metadata: None,
|
metadata: None,
|
||||||
result: None,
|
result: None,
|
||||||
listeners: vec!(),
|
listeners: vec!(),
|
||||||
|
@ -216,8 +250,6 @@ struct ResourceLoadInfo {
|
||||||
|
|
||||||
/// Implementation of the image cache
|
/// Implementation of the image cache
|
||||||
struct ImageCache {
|
struct ImageCache {
|
||||||
progress_sender: Sender<ResourceLoadInfo>,
|
|
||||||
|
|
||||||
decoder_sender: Sender<DecoderMsg>,
|
decoder_sender: Sender<DecoderMsg>,
|
||||||
|
|
||||||
// Worker threads for decoding images.
|
// Worker threads for decoding images.
|
||||||
|
@ -245,7 +277,6 @@ struct DecoderMsg {
|
||||||
struct Receivers {
|
struct Receivers {
|
||||||
cmd_receiver: Receiver<ImageCacheCommand>,
|
cmd_receiver: Receiver<ImageCacheCommand>,
|
||||||
decoder_receiver: Receiver<DecoderMsg>,
|
decoder_receiver: Receiver<DecoderMsg>,
|
||||||
progress_receiver: Receiver<ResourceLoadInfo>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Receivers {
|
impl Receivers {
|
||||||
|
@ -253,16 +284,12 @@ impl Receivers {
|
||||||
fn recv(&self) -> SelectResult {
|
fn recv(&self) -> SelectResult {
|
||||||
let cmd_receiver = &self.cmd_receiver;
|
let cmd_receiver = &self.cmd_receiver;
|
||||||
let decoder_receiver = &self.decoder_receiver;
|
let decoder_receiver = &self.decoder_receiver;
|
||||||
let progress_receiver = &self.progress_receiver;
|
|
||||||
select! {
|
select! {
|
||||||
msg = cmd_receiver.recv() => {
|
msg = cmd_receiver.recv() => {
|
||||||
SelectResult::Command(msg.unwrap())
|
SelectResult::Command(msg.unwrap())
|
||||||
},
|
},
|
||||||
msg = decoder_receiver.recv() => {
|
msg = decoder_receiver.recv() => {
|
||||||
SelectResult::Decoder(msg.unwrap())
|
SelectResult::Decoder(msg.unwrap())
|
||||||
},
|
|
||||||
msg = progress_receiver.recv() => {
|
|
||||||
SelectResult::Progress(msg.unwrap())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,7 +298,6 @@ impl Receivers {
|
||||||
/// The types of messages that the main image cache thread receives.
|
/// The types of messages that the main image cache thread receives.
|
||||||
enum SelectResult {
|
enum SelectResult {
|
||||||
Command(ImageCacheCommand),
|
Command(ImageCacheCommand),
|
||||||
Progress(ResourceLoadInfo),
|
|
||||||
Decoder(DecoderMsg),
|
Decoder(DecoderMsg),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,10 +342,8 @@ impl ImageCache {
|
||||||
// Ask the router to proxy messages received over IPC to us.
|
// Ask the router to proxy messages received over IPC to us.
|
||||||
let cmd_receiver = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_command_receiver);
|
let cmd_receiver = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_command_receiver);
|
||||||
|
|
||||||
let (progress_sender, progress_receiver) = channel();
|
|
||||||
let (decoder_sender, decoder_receiver) = channel();
|
let (decoder_sender, decoder_receiver) = channel();
|
||||||
let mut cache = ImageCache {
|
let mut cache = ImageCache {
|
||||||
progress_sender: progress_sender,
|
|
||||||
decoder_sender: decoder_sender,
|
decoder_sender: decoder_sender,
|
||||||
thread_pool: ThreadPool::new(4),
|
thread_pool: ThreadPool::new(4),
|
||||||
pending_loads: AllPendingLoads::new(),
|
pending_loads: AllPendingLoads::new(),
|
||||||
|
@ -331,7 +355,6 @@ impl ImageCache {
|
||||||
let receivers = Receivers {
|
let receivers = Receivers {
|
||||||
cmd_receiver: cmd_receiver,
|
cmd_receiver: cmd_receiver,
|
||||||
decoder_receiver: decoder_receiver,
|
decoder_receiver: decoder_receiver,
|
||||||
progress_receiver: progress_receiver,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut exit_sender: Option<IpcSender<()>> = None;
|
let mut exit_sender: Option<IpcSender<()>> = None;
|
||||||
|
@ -341,9 +364,6 @@ impl ImageCache {
|
||||||
SelectResult::Command(cmd) => {
|
SelectResult::Command(cmd) => {
|
||||||
exit_sender = cache.handle_cmd(cmd);
|
exit_sender = cache.handle_cmd(cmd);
|
||||||
}
|
}
|
||||||
SelectResult::Progress(msg) => {
|
|
||||||
cache.handle_progress(msg);
|
|
||||||
}
|
|
||||||
SelectResult::Decoder(msg) => {
|
SelectResult::Decoder(msg) => {
|
||||||
cache.handle_decoder(msg);
|
cache.handle_decoder(msg);
|
||||||
}
|
}
|
||||||
|
@ -395,14 +415,14 @@ impl ImageCache {
|
||||||
pending_load.bytes.extend_from_slice(&data);
|
pending_load.bytes.extend_from_slice(&data);
|
||||||
//jmr0 TODO: possibly move to another task?
|
//jmr0 TODO: possibly move to another task?
|
||||||
if let None = pending_load.metadata {
|
if let None = pending_load.metadata {
|
||||||
if let Ok(metadata) = load_from_buf(&pending_load.bytes) {
|
if let Ok(metadata) = load_from_buf(&pending_load.bytes.as_slice()) {
|
||||||
let dimensions = metadata.dimensions();
|
let dimensions = metadata.dimensions();
|
||||||
let img_metadata = ImageMetadata { width: dimensions.width,
|
let img_metadata = ImageMetadata { width: dimensions.width,
|
||||||
height: dimensions.height };
|
height: dimensions.height };
|
||||||
pending_load.metadata = Some(img_metadata.clone());
|
|
||||||
for listener in &pending_load.listeners {
|
for listener in &pending_load.listeners {
|
||||||
listener.respond(ImageResponse::MetadataLoaded(img_metadata.clone()));
|
listener.respond(ImageResponse::MetadataLoaded(img_metadata.clone()));
|
||||||
}
|
}
|
||||||
|
pending_load.metadata = Some(img_metadata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -411,15 +431,11 @@ impl ImageCache {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
let pending_load = self.pending_loads.get_by_key_mut(&msg.key).unwrap();
|
let pending_load = self.pending_loads.get_by_key_mut(&msg.key).unwrap();
|
||||||
pending_load.result = Some(result);
|
pending_load.result = Some(result);
|
||||||
let bytes = mem::replace(&mut pending_load.bytes, vec!());
|
let bytes = pending_load.bytes.mark_complete();
|
||||||
let sender = self.decoder_sender.clone();
|
let sender = self.decoder_sender.clone();
|
||||||
|
|
||||||
self.thread_pool.execute(move || {
|
self.thread_pool.execute(move || {
|
||||||
let image = load_from_memory(&bytes);
|
let msg = decode_bytes_sync(key, &*bytes);
|
||||||
let msg = DecoderMsg {
|
|
||||||
key: key,
|
|
||||||
image: image
|
|
||||||
};
|
|
||||||
sender.send(msg).unwrap();
|
sender.send(msg).unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -448,7 +464,10 @@ impl ImageCache {
|
||||||
|
|
||||||
// Change state of a url from pending -> loaded.
|
// Change state of a url from pending -> loaded.
|
||||||
fn complete_load(&mut self, key: LoadKey, mut load_result: LoadResult) {
|
fn complete_load(&mut self, key: LoadKey, mut load_result: LoadResult) {
|
||||||
let pending_load = self.pending_loads.remove(&key).unwrap();
|
let pending_load = match self.pending_loads.remove(&key) {
|
||||||
|
Some(load) => load,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
match load_result {
|
match load_result {
|
||||||
LoadResult::Loaded(ref mut image) => {
|
LoadResult::Loaded(ref mut image) => {
|
||||||
|
@ -502,39 +521,64 @@ impl ImageCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a completed image if it exists, or None if there is no complete load
|
/// Return a completed image if it exists, or None if there is no complete load
|
||||||
/// of the complete load is not fully decoded or is unavailable.
|
/// or the complete load is not fully decoded or is unavailable.
|
||||||
|
fn get_completed_image_if_available(&self,
|
||||||
|
url: &ServoUrl,
|
||||||
|
placeholder: UsePlaceholder)
|
||||||
|
-> Option<Result<ImageOrMetadataAvailable, ImageState>> {
|
||||||
|
self.completed_loads.get(url).map(|completed_load| {
|
||||||
|
match (&completed_load.image_response, placeholder) {
|
||||||
|
(&ImageResponse::Loaded(ref image), _) |
|
||||||
|
(&ImageResponse::PlaceholderLoaded(ref image), UsePlaceholder::Yes) => {
|
||||||
|
Ok(ImageOrMetadataAvailable::ImageAvailable(image.clone()))
|
||||||
|
}
|
||||||
|
(&ImageResponse::PlaceholderLoaded(_), UsePlaceholder::No) |
|
||||||
|
(&ImageResponse::None, _) |
|
||||||
|
(&ImageResponse::MetadataLoaded(_), _) => {
|
||||||
|
Err(ImageState::LoadError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return any available metadata or image for the given URL, or an indication that
|
||||||
|
/// the image is not yet available if it is in progress, or else reserve a slot in
|
||||||
|
/// the cache for the URL if the consumer can request images.
|
||||||
fn get_image_or_meta_if_available(&mut self,
|
fn get_image_or_meta_if_available(&mut self,
|
||||||
url: ServoUrl,
|
url: ServoUrl,
|
||||||
placeholder: UsePlaceholder,
|
placeholder: UsePlaceholder,
|
||||||
can_request: CanRequestImages)
|
can_request: CanRequestImages)
|
||||||
-> Result<ImageOrMetadataAvailable, ImageState> {
|
-> Result<ImageOrMetadataAvailable, ImageState> {
|
||||||
match self.completed_loads.get(&url) {
|
if let Some(result) = self.get_completed_image_if_available(&url, placeholder) {
|
||||||
Some(completed_load) => {
|
return result;
|
||||||
match (completed_load.image_response.clone(), placeholder) {
|
}
|
||||||
(ImageResponse::Loaded(image), _) |
|
|
||||||
(ImageResponse::PlaceholderLoaded(image), UsePlaceholder::Yes) => {
|
let decoded = {
|
||||||
Ok(ImageOrMetadataAvailable::ImageAvailable(image))
|
let result = self.pending_loads.get_cached(url.clone(), can_request);
|
||||||
}
|
match result {
|
||||||
(ImageResponse::PlaceholderLoaded(_), UsePlaceholder::No) |
|
CacheResult::Hit(key, pl) => match (&pl.result, &pl.metadata) {
|
||||||
(ImageResponse::None, _) |
|
(&Some(Ok(_)), _) =>
|
||||||
(ImageResponse::MetadataLoaded(_), _) => {
|
decode_bytes_sync(key, &pl.bytes.as_slice()),
|
||||||
Err(ImageState::LoadError)
|
(&None, &Some(ref meta)) =>
|
||||||
}
|
return Ok(ImageOrMetadataAvailable::MetadataAvailable(meta.clone())),
|
||||||
}
|
(&Some(Err(_)), _) | (&None, &None) =>
|
||||||
}
|
return Err(ImageState::Pending(key)),
|
||||||
None => {
|
},
|
||||||
let result = self.pending_loads.get_cached(url, can_request);
|
CacheResult::Miss(Some((key, _pl))) =>
|
||||||
match result {
|
return Err(ImageState::NotRequested(key)),
|
||||||
CacheResult::Hit(key, pl) => match pl.metadata {
|
CacheResult::Miss(None) =>
|
||||||
Some(ref meta) =>
|
return Err(ImageState::LoadError),
|
||||||
Ok(ImageOrMetadataAvailable::MetadataAvailable(meta.clone())),
|
|
||||||
None =>
|
|
||||||
Err(ImageState::Pending(key)),
|
|
||||||
},
|
|
||||||
CacheResult::Miss(Some((key, _pl))) => Err(ImageState::NotRequested(key)),
|
|
||||||
CacheResult::Miss(None) => Err(ImageState::LoadError),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// In the case where a decode is ongoing (or waiting in a queue) but we have the
|
||||||
|
// full response available, we decode the bytes synchronously and ignore the
|
||||||
|
// async decode when it finishes later.
|
||||||
|
// TODO: make this behaviour configurable according to the caller's needs.
|
||||||
|
self.handle_decoder(decoded);
|
||||||
|
match self.get_completed_image_if_available(&url, placeholder) {
|
||||||
|
Some(result) => result,
|
||||||
|
None => Err(ImageState::LoadError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -542,12 +586,12 @@ impl ImageCache {
|
||||||
id: PendingImageId,
|
id: PendingImageId,
|
||||||
loaded_bytes: Vec<u8>) {
|
loaded_bytes: Vec<u8>) {
|
||||||
let action = FetchResponseMsg::ProcessResponseChunk(loaded_bytes);
|
let action = FetchResponseMsg::ProcessResponseChunk(loaded_bytes);
|
||||||
let _ = self.progress_sender.send(ResourceLoadInfo {
|
self.handle_progress(ResourceLoadInfo {
|
||||||
action: action,
|
action: action,
|
||||||
key: id,
|
key: id,
|
||||||
});
|
});
|
||||||
let action = FetchResponseMsg::ProcessResponseEOF(Ok(()));
|
let action = FetchResponseMsg::ProcessResponseEOF(Ok(()));
|
||||||
let _ = self.progress_sender.send(ResourceLoadInfo {
|
self.handle_progress(ResourceLoadInfo {
|
||||||
action: action,
|
action: action,
|
||||||
key: id,
|
key: id,
|
||||||
});
|
});
|
||||||
|
@ -564,3 +608,11 @@ pub fn new_image_cache_thread(webrender_api: webrender_traits::RenderApi) -> Ima
|
||||||
|
|
||||||
ImageCacheThread::new(ipc_command_sender)
|
ImageCacheThread::new(ipc_command_sender)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn decode_bytes_sync(key: LoadKey, bytes: &[u8]) -> DecoderMsg {
|
||||||
|
let image = load_from_memory(bytes);
|
||||||
|
DecoderMsg {
|
||||||
|
key: key,
|
||||||
|
image: image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue