mirror of
https://github.com/servo/servo.git
synced 2025-08-02 20:20:14 +01:00
Implemented the plumbing for paint worklets.
This commit is contained in:
parent
7e273d6c9b
commit
fd17dcd604
23 changed files with 600 additions and 156 deletions
|
@ -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;
|
||||
|
|
97
components/script/dom/paintworkletglobalscope.rs
Normal file
97
components/script/dom/paintworkletglobalscope.rs
Normal file
|
@ -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>>)
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()))
|
||||
}
|
||||
|
@ -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 {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
//! thread pool implementation, which only performs GC or code loading on
|
||||
//! a backup thread, not on the primary worklet thread.
|
||||
|
||||
use app_units::Au;
|
||||
use dom::bindings::codegen::Bindings::RequestBinding::RequestCredentials;
|
||||
use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
|
||||
use dom::bindings::codegen::Bindings::WorkletBinding::WorkletMethods;
|
||||
|
@ -27,6 +28,7 @@ use dom::bindings::str::USVString;
|
|||
use dom::bindings::trace::JSTraceable;
|
||||
use dom::bindings::trace::RootedTraceableBox;
|
||||
use dom::globalscope::GlobalScope;
|
||||
use dom::paintworkletglobalscope::PaintWorkletTask;
|
||||
use dom::promise::Promise;
|
||||
use dom::testworkletglobalscope::TestWorkletTask;
|
||||
use dom::window::Window;
|
||||
|
@ -35,6 +37,7 @@ use dom::workletglobalscope::WorkletGlobalScopeInit;
|
|||
use dom::workletglobalscope::WorkletGlobalScopeType;
|
||||
use dom::workletglobalscope::WorkletTask;
|
||||
use dom_struct::dom_struct;
|
||||
use euclid::Size2D;
|
||||
use js::jsapi::JSGCParamKey;
|
||||
use js::jsapi::JSTracer;
|
||||
use js::jsapi::JS_GC;
|
||||
|
@ -42,6 +45,7 @@ use js::jsapi::JS_GetGCParameter;
|
|||
use js::rust::Runtime;
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use net_traits::IpcSend;
|
||||
use net_traits::image::base::Image;
|
||||
use net_traits::load_whole_resource;
|
||||
use net_traits::request::Destination;
|
||||
use net_traits::request::RequestInit;
|
||||
|
@ -54,6 +58,9 @@ use script_runtime::new_rt_and_cx;
|
|||
use script_thread::MainThreadScriptMsg;
|
||||
use script_thread::Runnable;
|
||||
use script_thread::ScriptThread;
|
||||
use script_traits::PaintWorkletError;
|
||||
use script_traits::PaintWorkletExecutor;
|
||||
use servo_atoms::Atom;
|
||||
use servo_rand;
|
||||
use servo_url::ImmutableOrigin;
|
||||
use servo_url::ServoUrl;
|
||||
|
@ -62,12 +69,14 @@ use std::collections::HashMap;
|
|||
use std::collections::hash_map;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::atomic::AtomicIsize;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::mpsc::Receiver;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use style::thread_state;
|
||||
use swapper::Swapper;
|
||||
use swapper::swapper;
|
||||
|
@ -76,6 +85,7 @@ use uuid::Uuid;
|
|||
// Magic numbers
|
||||
const WORKLET_THREAD_POOL_SIZE: u32 = 3;
|
||||
const MIN_GC_THRESHOLD: u32 = 1_000_000;
|
||||
const PAINT_TIMEOUT_MILLISECONDS: u64 = 10;
|
||||
|
||||
#[dom_struct]
|
||||
/// https://drafts.css-houdini.org/worklets/#worklet
|
||||
|
@ -109,6 +119,13 @@ impl Worklet {
|
|||
pub fn worklet_global_scope_type(&self) -> WorkletGlobalScopeType {
|
||||
self.global_type
|
||||
}
|
||||
|
||||
pub fn executor(&self) -> WorkletExecutor {
|
||||
WorkletExecutor {
|
||||
worklet_id: self.worklet_id,
|
||||
primary_sender: Mutex::new(ScriptThread::worklet_thread_pool().primary_sender.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WorkletMethods for Worklet {
|
||||
|
@ -561,7 +578,8 @@ impl WorkletThread {
|
|||
// TODO: Caching.
|
||||
// TODO: Avoid re-parsing the origin as a URL.
|
||||
let resource_fetcher = self.global_init.resource_threads.sender();
|
||||
let origin_url = ServoUrl::parse(&*origin.unicode_serialization()).expect("Failed to parse origin as URL.");
|
||||
let origin_url = ServoUrl::parse(&*origin.unicode_serialization())
|
||||
.unwrap_or_else(|_| ServoUrl::parse("about:blank").unwrap());
|
||||
let request = RequestInit {
|
||||
url: script_url,
|
||||
type_: RequestType::Script,
|
||||
|
@ -635,3 +653,35 @@ impl WorkletThread {
|
|||
self.script_sender.send(msg).expect("Worklet thread outlived script thread.");
|
||||
}
|
||||
}
|
||||
|
||||
/// An executor of worklet tasks
|
||||
pub struct WorkletExecutor {
|
||||
worklet_id: WorkletId,
|
||||
// Rather annoyingly, we have to use a mutex here because
|
||||
// layout threads share their context rather than cloning it.
|
||||
primary_sender: Mutex<Sender<WorkletData>>,
|
||||
}
|
||||
|
||||
impl WorkletExecutor {
|
||||
/// Schedule a worklet task to be peformed by the worklet thread pool.
|
||||
fn schedule_a_worklet_task(&self, task: WorkletTask) {
|
||||
let _ = self.primary_sender.lock()
|
||||
.expect("Locking the worklet channel.")
|
||||
.send(WorkletData::Task(self.worklet_id, task));
|
||||
}
|
||||
}
|
||||
|
||||
impl PaintWorkletExecutor for WorkletExecutor {
|
||||
/// https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image
|
||||
fn draw_a_paint_image(&self,
|
||||
name: Atom,
|
||||
concrete_object_size: Size2D<Au>)
|
||||
-> Result<Image, PaintWorkletError>
|
||||
{
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let task = WorkletTask::Paint(PaintWorkletTask::DrawAPaintImage(name, concrete_object_size, sender));
|
||||
let timeout = Duration::from_millis(PAINT_TIMEOUT_MILLISECONDS);
|
||||
self.schedule_a_worklet_task(task);
|
||||
receiver.recv_timeout(timeout)?
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ use devtools_traits::ScriptToDevtoolsControlMsg;
|
|||
use dom::bindings::inheritance::Castable;
|
||||
use dom::bindings::js::Root;
|
||||
use dom::globalscope::GlobalScope;
|
||||
use dom::paintworkletglobalscope::PaintWorkletGlobalScope;
|
||||
use dom::paintworkletglobalscope::PaintWorkletTask;
|
||||
use dom::testworkletglobalscope::TestWorkletGlobalScope;
|
||||
use dom::testworkletglobalscope::TestWorkletTask;
|
||||
use dom_struct::dom_struct;
|
||||
|
@ -92,6 +94,10 @@ impl WorkletGlobalScope {
|
|||
Some(global) => global.perform_a_worklet_task(task),
|
||||
None => warn!("This is not a test worklet."),
|
||||
},
|
||||
WorkletTask::Paint(task) => match self.downcast::<PaintWorkletGlobalScope>() {
|
||||
Some(global) => global.perform_a_worklet_task(task),
|
||||
None => warn!("This is not a paint worklet."),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,8 +122,10 @@ pub struct WorkletGlobalScopeInit {
|
|||
/// https://drafts.css-houdini.org/worklets/#worklet-global-scope-type
|
||||
#[derive(Clone, Copy, Debug, HeapSizeOf, JSTraceable)]
|
||||
pub enum WorkletGlobalScopeType {
|
||||
/// https://drafts.css-houdini.org/worklets/#examples
|
||||
/// A servo-specific testing worklet
|
||||
Test,
|
||||
/// A paint worklet
|
||||
Paint,
|
||||
}
|
||||
|
||||
impl WorkletGlobalScopeType {
|
||||
|
@ -132,6 +140,8 @@ impl WorkletGlobalScopeType {
|
|||
match *self {
|
||||
WorkletGlobalScopeType::Test =>
|
||||
Root::upcast(TestWorkletGlobalScope::new(runtime, pipeline_id, base_url, init)),
|
||||
WorkletGlobalScopeType::Paint =>
|
||||
Root::upcast(PaintWorkletGlobalScope::new(runtime, pipeline_id, base_url, init)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,5 +149,5 @@ impl WorkletGlobalScopeType {
|
|||
/// A task which can be performed in the context of a worklet global.
|
||||
pub enum WorkletTask {
|
||||
Test(TestWorkletTask),
|
||||
Paint(PaintWorkletTask),
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue