Skip to content

Commit

Permalink
Implemented the plumbing for paint worklets.
Browse files Browse the repository at this point in the history
  • Loading branch information
Alan Jeffrey committed Jun 7, 2017
1 parent 7e273d6 commit fd17dcd
Show file tree
Hide file tree
Showing 23 changed files with 595 additions and 151 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions components/layout/context.rs
Expand Up @@ -15,6 +15,7 @@ use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
use opaque_node::OpaqueNodeMethods;
use parking_lot::RwLock;
use script_layout_interface::{PendingImage, PendingImageState};
use script_traits::PaintWorkletExecutor;
use script_traits::UntrustedNodeAddress;
use servo_url::ServoUrl;
use std::borrow::{Borrow, BorrowMut};
Expand Down Expand Up @@ -95,6 +96,9 @@ pub struct LayoutContext<'a> {
WebRenderImageInfo,
BuildHasherDefault<FnvHasher>>>>,

/// The executor for worklets
pub paint_worklet_executor: Option<Arc<PaintWorkletExecutor>>,

/// 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>>>,
Expand Down
347 changes: 221 additions & 126 deletions components/layout/display_list_builder.rs

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions components/layout_thread/lib.rs
Expand Up @@ -90,6 +90,7 @@ use script_layout_interface::rpc::TextIndexResponse;
use script_layout_interface::wrapper_traits::LayoutNode;
use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
use script_traits::{ScrollState, UntrustedNodeAddress};
use script_traits::PaintWorkletExecutor;
use selectors::Element;
use servo_config::opts;
use servo_config::prefs::PREFS;
Expand Down Expand Up @@ -225,6 +226,9 @@ pub struct LayoutThread {

webrender_image_cache: Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder),
WebRenderImageInfo>>>,
/// The executor for paint worklets.
/// Will be None if the script thread hasn't added any paint worklet modules.
paint_worklet_executor: Option<Arc<PaintWorkletExecutor>>,

/// Webrender interface.
webrender_api: webrender_traits::RenderApi,
Expand Down Expand Up @@ -477,6 +481,7 @@ impl LayoutThread {
constellation_chan: constellation_chan.clone(),
time_profiler_chan: time_profiler_chan,
mem_profiler_chan: mem_profiler_chan,
paint_worklet_executor: None,
image_cache: image_cache.clone(),
font_cache_thread: font_cache_thread,
first_reflow: Cell::new(true),
Expand Down Expand Up @@ -574,6 +579,7 @@ impl LayoutThread {
webrender_image_cache: self.webrender_image_cache.clone(),
pending_images: if script_initiated_layout { Some(Mutex::new(vec![])) } else { None },
newly_transitioning_nodes: if script_initiated_layout { Some(Mutex::new(vec![])) } else { None },
paint_worklet_executor: self.paint_worklet_executor.clone(),
}
}

Expand Down Expand Up @@ -689,6 +695,11 @@ impl LayoutThread {
Msg::SetFinalUrl(final_url) => {
self.url = final_url;
},
Msg::SetPaintWorkletExecutor(executor) => {
debug!("Setting the paint worklet executor");
debug_assert!(self.paint_worklet_executor.is_none());
self.paint_worklet_executor = Some(executor);
},
Msg::PrepareToExit(response_chan) => {
self.prepare_to_exit(response_chan);
return false
Expand Down
36 changes: 15 additions & 21 deletions components/net/image_cache.rs
Expand Up @@ -57,9 +57,18 @@ fn get_placeholder_image(webrender_api: &webrender_traits::RenderApi, path: &Pat
let mut image_data = vec![];
try!(file.read_to_end(&mut image_data));
let mut image = load_from_memory(&image_data).unwrap();
set_webrender_image_key(webrender_api, &mut image);
Ok(Arc::new(image))
}

fn set_webrender_image_key(webrender_api: &webrender_traits::RenderApi, image: &mut Image) {
if image.id.is_some() { return; }
let format = convert_format(image.format);
let mut bytes = Vec::new();
bytes.extend_from_slice(&*image.bytes);
if format == webrender_traits::ImageFormat::RGBA8 {
premultiply(bytes.as_mut_slice());
}
let descriptor = webrender_traits::ImageDescriptor {
width: image.width,
height: image.height,
Expand All @@ -72,7 +81,6 @@ fn get_placeholder_image(webrender_api: &webrender_traits::RenderApi, path: &Pat
let image_key = webrender_api.generate_image_key();
webrender_api.add_image(image_key, descriptor, data, None);
image.id = Some(image_key);
Ok(Arc::new(image))
}

// TODO(gw): This is a port of the old is_image_opaque code from WR.
Expand Down Expand Up @@ -338,26 +346,7 @@ impl ImageCacheStore {
};

match load_result {
LoadResult::Loaded(ref mut image) => {
let format = convert_format(image.format);
let mut bytes = Vec::new();
bytes.extend_from_slice(&*image.bytes);
if format == webrender_traits::ImageFormat::RGBA8 {
premultiply(bytes.as_mut_slice());
}
let descriptor = webrender_traits::ImageDescriptor {
width: image.width,
height: image.height,
stride: None,
format: format,
offset: 0,
is_opaque: is_image_opaque(format, &bytes),
};
let data = webrender_traits::ImageData::new(bytes);
let image_key = self.webrender_api.generate_image_key();
self.webrender_api.add_image(image_key, descriptor, data, None);
image.id = Some(image_key);
}
LoadResult::Loaded(ref mut image) => set_webrender_image_key(&self.webrender_api, image),
LoadResult::PlaceholderLoaded(..) | LoadResult::None => {}
}

Expand Down Expand Up @@ -576,4 +565,9 @@ impl ImageCache for ImageCacheImpl {
}
}
}

/// Ensure an image has a webrender key.
fn set_webrender_image_key(&self, image: &mut Image) {
set_webrender_image_key(&self.store.lock().unwrap().webrender_api, image);
}
}
3 changes: 3 additions & 0 deletions components/net_traits/image_cache.rs
Expand Up @@ -118,4 +118,7 @@ pub trait ImageCache: Sync + Send {

/// Inform the image cache about a response for a pending request.
fn notify_pending_response(&self, id: PendingImageId, action: FetchResponseMsg);

/// Ensure an image has a webrender key.
fn set_webrender_image_key(&self, image: &mut Image);
}
1 change: 1 addition & 0 deletions components/script/dom/mod.rs
Expand Up @@ -391,6 +391,7 @@ pub mod node;
pub mod nodeiterator;
pub mod nodelist;
pub mod pagetransitionevent;
pub mod paintworkletglobalscope;
pub mod performance;
pub mod performancetiming;
pub mod permissions;
Expand Down
97 changes: 97 additions & 0 deletions components/script/dom/paintworkletglobalscope.rs
@@ -0,0 +1,97 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use app_units::Au;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::PaintWorkletGlobalScopeBinding;
use dom::bindings::codegen::Bindings::PaintWorkletGlobalScopeBinding::PaintWorkletGlobalScopeMethods;
use dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
use dom::bindings::js::Root;
use dom::bindings::str::DOMString;
use dom::workletglobalscope::WorkletGlobalScope;
use dom::workletglobalscope::WorkletGlobalScopeInit;
use dom_struct::dom_struct;
use euclid::Size2D;
use ipc_channel::ipc::IpcSharedMemory;
use js::rust::Runtime;
use msg::constellation_msg::PipelineId;
use net_traits::image::base::Image;
use net_traits::image::base::PixelFormat;
use script_traits::PaintWorkletError;
use servo_atoms::Atom;
use servo_url::ServoUrl;
use std::rc::Rc;
use std::sync::mpsc::Sender;

#[dom_struct]
/// https://drafts.css-houdini.org/css-paint-api/#paintworkletglobalscope
pub struct PaintWorkletGlobalScope {
/// The worklet global for this object
worklet_global: WorkletGlobalScope,
/// A buffer to draw into
buffer: DOMRefCell<Vec<u8>>,
}

impl PaintWorkletGlobalScope {
#[allow(unsafe_code)]
pub fn new(runtime: &Runtime,
pipeline_id: PipelineId,
base_url: ServoUrl,
init: &WorkletGlobalScopeInit)
-> Root<PaintWorkletGlobalScope> {
debug!("Creating paint worklet global scope for pipeline {}.", pipeline_id);
let global = box PaintWorkletGlobalScope {
worklet_global: WorkletGlobalScope::new_inherited(pipeline_id, base_url, init),
buffer: Default::default(),
};
unsafe { PaintWorkletGlobalScopeBinding::Wrap(runtime.cx(), global) }
}

pub fn perform_a_worklet_task(&self, task: PaintWorkletTask) {
match task {
PaintWorkletTask::DrawAPaintImage(name, size, sender) => self.draw_a_paint_image(name, size, sender),
}
}

fn draw_a_paint_image(&self,
name: Atom,
concrete_object_size: Size2D<Au>,
sender: Sender<Result<Image, PaintWorkletError>>) {
let width = concrete_object_size.width.to_px().abs() as u32;
let height = concrete_object_size.height.to_px().abs() as u32;
let area = (width as usize) * (height as usize);
let old_buffer_size = self.buffer.borrow().len();
let new_buffer_size = area * 4;
debug!("Drawing a paint image {}({},{}).", name, width, height);
// TODO: call into script to create the image.
// For now, we just build a dummy.
if new_buffer_size > old_buffer_size {
let pixel = [0xFF, 0x00, 0x00, 0xFF];
self.buffer.borrow_mut().extend(pixel.iter().cycle().take(new_buffer_size - old_buffer_size));
} else {
self.buffer.borrow_mut().truncate(new_buffer_size);
}
let image = Image {
width: width,
height: height,
format: PixelFormat::RGBA8,
bytes: IpcSharedMemory::from_bytes(&*self.buffer.borrow()),
id: None,
};
let _ = sender.send(Ok(image));
}
}

impl PaintWorkletGlobalScopeMethods for PaintWorkletGlobalScope {
/// https://drafts.css-houdini.org/css-paint-api/#dom-paintworkletglobalscope-registerpaint
fn RegisterPaint(&self, name: DOMString, _paintCtor: Rc<VoidFunction>) {
debug!("Registering paint image name {}.", name);
// TODO
}
}

/// Tasks which can be peformed by a paint worklet
pub enum PaintWorkletTask {
DrawAPaintImage(Atom, Size2D<Au>, Sender<Result<Image, PaintWorkletError>>)
}
9 changes: 9 additions & 0 deletions components/script/dom/webidls/PaintWorkletGlobalScope.webidl
@@ -0,0 +1,9 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// https://drafts.css-houdini.org/css-paint-api/#paintworkletglobalscope
[Global=(Worklet,PaintWorklet), Exposed=PaintWorklet]
interface PaintWorkletGlobalScope : WorkletGlobalScope {
void registerPaint(DOMString name, VoidFunction paintCtor);
};
4 changes: 4 additions & 0 deletions components/script/dom/webidls/Window.webidl
Expand Up @@ -204,3 +204,7 @@ partial interface Window {
//readonly attribute EventSender eventSender;
};

// https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet
partial interface Window {
[SameObject] readonly attribute Worklet paintWorklet;
};
17 changes: 17 additions & 0 deletions components/script/dom/window.rs
Expand Up @@ -51,6 +51,7 @@ use dom::storage::Storage;
use dom::testrunner::TestRunner;
use dom::windowproxy::WindowProxy;
use dom::worklet::Worklet;
use dom::workletglobalscope::WorkletGlobalScopeType;
use dom_struct::dom_struct;
use euclid::{Point2D, Rect, Size2D};
use fetch;
Expand Down Expand Up @@ -279,6 +280,8 @@ pub struct Window {

/// Worklets
test_worklet: MutNullableJS<Worklet>,
/// https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet
paint_worklet: MutNullableJS<Worklet>,
}

impl Window {
Expand Down Expand Up @@ -373,6 +376,14 @@ impl Window {
self.webvr_thread.clone()
}

fn new_paint_worklet(&self) -> Root<Worklet> {
debug!("Creating new paint worklet.");
let worklet = Worklet::new(self, WorkletGlobalScopeType::Paint);
let executor = Arc::new(worklet.executor());
let _ = self.layout_chan.send(Msg::SetPaintWorkletExecutor(executor));
worklet
}

pub fn permission_state_invocation_results(&self) -> &DOMRefCell<HashMap<String, PermissionState>> {
&self.permission_state_invocation_results
}
Expand Down Expand Up @@ -1011,6 +1022,11 @@ impl WindowMethods for Window {
fetch::Fetch(&self.upcast(), input, init)
}

// https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet
fn PaintWorklet(&self) -> Root<Worklet> {
self.paint_worklet.or_init(|| self.new_paint_worklet())
}

fn TestRunner(&self) -> Root<TestRunner> {
self.test_runner.or_init(|| TestRunner::new(self.upcast()))
}
Expand Down Expand Up @@ -1856,6 +1872,7 @@ impl Window {
pending_layout_images: DOMRefCell::new(HashMap::new()),
unminified_js_dir: DOMRefCell::new(None),
test_worklet: Default::default(),
paint_worklet: Default::default(),
};

unsafe {
Expand Down

0 comments on commit fd17dcd

Please sign in to comment.