Skip to content

Commit

Permalink
Start using on_refresh_driver_tick servo#5681
Browse files Browse the repository at this point in the history
RequestAnimationFrame
  • Loading branch information
gurobokum committed Apr 20, 2015
1 parent 40ee41c commit d24c73a
Show file tree
Hide file tree
Showing 12 changed files with 187 additions and 33 deletions.
86 changes: 61 additions & 25 deletions components/devtools/actors/framerate.rs
Expand Up @@ -5,15 +5,23 @@
use rustc_serialize::json;
use std::mem;
use std::net::TcpStream;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Sender, channel};
use time::precise_time_ns;

use msg::constellation_msg::PipelineId;
use actor::{Actor, ActorRegistry};
use actors::timeline::HighResolutionStamp;
use devtools_traits::DevtoolScriptControlMsg;
use util::task;

pub struct FramerateActor {
name: String,
pipeline: PipelineId,
script_sender: Sender<DevtoolScriptControlMsg>,
start_time: Option<u64>,
is_recording: bool,
ticks: Vec<u64>,
is_recording: Arc<Mutex<bool>>,
ticks: Arc<Mutex<Vec<HighResolutionStamp>>>,
}

impl Actor for FramerateActor {
Expand All @@ -33,50 +41,78 @@ impl Actor for FramerateActor {

impl FramerateActor {
/// return name of actor
pub fn create(registry: &ActorRegistry) -> String {
pub fn create(registry: &ActorRegistry,
pipeline_id: PipelineId,
script_sender: Sender<DevtoolScriptControlMsg>) -> String {
let actor_name = registry.new_name("framerate");
let mut actor = FramerateActor {
name: actor_name.clone(),
pipeline: pipeline_id,
script_sender: script_sender,
start_time: None,
is_recording: false,
ticks: Vec::new(),
is_recording: Arc::new(Mutex::new(false)),
ticks: Arc::new(Mutex::new(Vec::new())),
};

actor.start_recording();
registry.register_later(box actor);
actor_name
}

// callback on request animation frame
#[allow(dead_code)]
pub fn on_refresh_driver_tick(&mut self) {
if !self.is_recording {
return;
}
// TODO: Need implement requesting animation frame
// http://hg.mozilla.org/mozilla-central/file/0a46652bd992/dom/base/nsGlobalWindow.cpp#l5314

let start_time = self.start_time.as_ref().unwrap();
self.ticks.push(*start_time - precise_time_ns());
}

pub fn take_pending_ticks(&mut self) -> Vec<u64> {
mem::replace(&mut self.ticks, Vec::new())
pub fn take_pending_ticks(&mut self) -> Vec<HighResolutionStamp> {
let mut lock = self.ticks.lock();
let mut ticks = lock.as_mut().unwrap();
mem::replace(ticks, Vec::new())
}

fn start_recording(&mut self) {
self.is_recording = true;
let is_recording = self.is_recording.clone();
let script_sender = self.script_sender.clone();
let ticks = self.ticks.clone();
let pipeline = self.pipeline.clone();

let mut lock = self.is_recording.lock();
**lock.as_mut().unwrap() = true;
self.start_time = Some(precise_time_ns());

// TODO(#5681): Need implement requesting animation frame
// http://hg.mozilla.org/mozilla-central/file/0a46652bd992/dom/base/nsGlobalWindow.cpp#l5314
let (rx, tx) = channel::<HighResolutionStamp>();
let rx_copy = rx.clone();

task::spawn_named("Framerate worker".to_string(), move || {
loop {
if !*is_recording.lock().unwrap() {
break;
}

match tx.try_recv() {
Ok(stamp) => {
let mut lock = ticks.lock();
let rx = rx_copy.clone();
lock.as_mut().unwrap().push(stamp);

let closure = move |now: f64| {
rx.send(HighResolutionStamp::wrap(now)).unwrap();
};
script_sender.send(DevtoolScriptControlMsg::RequestAnimationFrame(pipeline, Box::new(closure))).unwrap();
},
Err(_) => (),
}
}
});

let closure = move |now: f64| {
rx.send(HighResolutionStamp::wrap(now)).unwrap();
};
self.script_sender.send(DevtoolScriptControlMsg::RequestAnimationFrame(self.pipeline, Box::new(closure))).unwrap();
}

fn stop_recording(&mut self) {
if !self.is_recording {
let mut lock = self.is_recording.lock();
let mut is_recording = lock.as_mut().unwrap();
if !**is_recording {
return;
}
self.is_recording = false;
**is_recording = false;
self.start_time = None;
}

Expand Down
15 changes: 11 additions & 4 deletions components/devtools/actors/timeline.rs
Expand Up @@ -94,21 +94,25 @@ struct FramerateEmitterReply {
__type__: String,
from: String,
delta: HighResolutionStamp,
timestamps: Vec<u64>,
timestamps: Vec<HighResolutionStamp>,
}

/// HighResolutionStamp is struct that contains duration in milliseconds
/// with accuracy to microsecond that shows how much time has passed since
/// actor registry inited
/// analog https://w3c.github.io/hr-time/#sec-DOMHighResTimeStamp
struct HighResolutionStamp(f64);
pub struct HighResolutionStamp(f64);

impl HighResolutionStamp {
fn new(start_stamp: PreciseTime, time: PreciseTime) -> HighResolutionStamp {
pub fn new(start_stamp: PreciseTime, time: PreciseTime) -> HighResolutionStamp {
let duration = start_stamp.to(time).num_microseconds()
.expect("Too big duration in microseconds");
HighResolutionStamp(duration as f64 / 1000 as f64)
}

pub fn wrap(time: f64) -> HighResolutionStamp {
HighResolutionStamp(time)
}
}

impl Encodable for HighResolutionStamp {
Expand Down Expand Up @@ -249,7 +253,10 @@ impl Actor for TimelineActor {
// init framerate actor
if let Some(with_ticks) = msg.get("withTicks") {
if let Some(true) = with_ticks.as_boolean() {
*self.framerate_actor.borrow_mut() = Some(FramerateActor::create(registry));
let framerate_actor = Some(FramerateActor::create(registry,
self.pipeline.clone(),
self.script_sender.clone()));
*self.framerate_actor.borrow_mut() = framerate_actor;
}
}

Expand Down
2 changes: 2 additions & 0 deletions components/devtools_traits/lib.rs
Expand Up @@ -10,6 +10,7 @@
#![crate_type = "rlib"]

#![feature(net)]
#![feature(unboxed_closures)]

#![allow(non_snake_case)]

Expand Down Expand Up @@ -117,6 +118,7 @@ pub enum DevtoolScriptControlMsg {
WantsLiveNotifications(PipelineId, bool),
SetTimelineMarkers(PipelineId, Vec<TimelineMarkerType>, Sender<TimelineMarker>),
DropTimelineMarkers(PipelineId, Vec<TimelineMarkerType>),
RequestAnimationFrame(PipelineId, Box<Fn(f64, ) + Send>),
}

/// Messages to instruct devtools server to update its state relating to a particular
Expand Down
4 changes: 4 additions & 0 deletions components/layout/animation.rs
Expand Up @@ -12,6 +12,7 @@ use gfx::display_list::OpaqueNode;
use layout_task::{LayoutTask, LayoutTaskData};
use msg::constellation_msg::{Msg, PipelineId};
use script::layout_interface::Animation;
use script_traits::{ConstellationControlMsg, ScriptControlChan};
use std::mem;
use std::sync::mpsc::Sender;
use style::animation::{GetMod, PropertyAnimation};
Expand Down Expand Up @@ -100,5 +101,8 @@ pub fn tick_all_animations(layout_task: &LayoutTask, rw_data: &mut LayoutTaskDat
rw_data.running_animations.push(running_animation)
}
}

let ScriptControlChan(ref chan) = layout_task.script_chan;
chan.send(ConstellationControlMsg::TickAllAnimations(layout_task.id)).unwrap();
}

7 changes: 7 additions & 0 deletions components/script/dom/bindings/trace.rs
Expand Up @@ -279,6 +279,13 @@ impl JSTraceable for Box<ScriptChan+Send> {
}
}

impl JSTraceable for Box<Fn(f64, )> {
#[inline]
fn trace(&self, _trc: *mut JSTracer) {
// Do nothing
}
}

impl<'a> JSTraceable for &'a str {
#[inline]
fn trace(&self, _: *mut JSTracer) {
Expand Down
48 changes: 47 additions & 1 deletion components/script/dom/document.rs
Expand Up @@ -11,6 +11,8 @@ use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter;
use dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::codegen::InheritTypes::{DocumentDerived, EventCast, HTMLElementCast};
use dom::bindings::codegen::InheritTypes::{HTMLHeadElementCast, TextCast, ElementCast};
use dom::bindings::codegen::InheritTypes::{DocumentTypeCast, HTMLHtmlElementCast, NodeCast};
Expand Down Expand Up @@ -81,11 +83,12 @@ use string_cache::{Atom, QualName};
use url::Url;
use js::jsapi::JSRuntime;

use core::iter::FromIterator;
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::ascii::AsciiExt;
use std::cell::{Cell, Ref};
use std::cell::{Cell, Ref, RefCell};
use std::default::Default;
use std::sync::mpsc::channel;
use std::num::ToPrimitive;
Expand Down Expand Up @@ -129,6 +132,12 @@ pub struct Document {
/// https://html.spec.whatwg.org/multipage/#concept-n-noscript
/// True if scripting is enabled for all scripts in this document
scripting_enabled: Cell<bool>,
/// https://html.spec.whatwg.org/multipage/webappapis.html#animation-frame-callback-identifier
/// Current identifier of animation frame callback
animation_frame_ident: RefCell<i32>,
/// https://html.spec.whatwg.org/multipage/webappapis.html#list-of-animation-frame-callbacks
/// List of animation frame callbacks
animation_frame_list: RefCell<HashMap<i32, Box<Fn(f64)>>>,
}

impl DocumentDerived for EventTarget {
Expand Down Expand Up @@ -235,6 +244,9 @@ pub trait DocumentHelpers<'a> {

fn set_current_script(self, script: Option<JSRef<HTMLScriptElement>>);
fn trigger_mozbrowser_event(self, event: MozBrowserEvent);
fn request_animation_frame(self, callback: Box<Fn(f64, )>) -> i32;
fn cancel_animation_frame(self, ident: i32);
fn run_animations(self);
}

impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
Expand Down Expand Up @@ -750,6 +762,38 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
}
}
}

fn request_animation_frame(self, callback: Box<Fn(f64, )>) -> i32 {
let window = self.window.root();
let window = window.r();
*self.animation_frame_ident.borrow_mut() += 1;
let ident = *self.animation_frame_ident.borrow();
self.animation_frame_list.borrow_mut().insert(ident, callback);

// TODO: Should tick animation only when document is visible
window.layout_chan().0.send(Msg::TickAnimations).unwrap();

ident
}

fn cancel_animation_frame(self, ident: i32) {
self.animation_frame_list.borrow_mut().remove(&ident);
}

/// https://html.spec.whatwg.org/multipage/webappapis.html#run-the-animation-frame-callbacks
/// http://www.w3.org/TR/animation-timing/#dfn-animation-task-source
fn run_animations(self) {
let mut animation_frame_list = self.animation_frame_list.borrow_mut();
let animation_frame_list = Vec::from_iter(animation_frame_list.drain());
let window = self.window.root();
let window = window.r();
let performance = window.Performance().root();
let performance = performance.r();

for (_, callback) in animation_frame_list {
callback(*performance.Now());
}
}
}

#[derive(PartialEq)]
Expand Down Expand Up @@ -821,6 +865,8 @@ impl Document {
focused: Default::default(),
current_script: Default::default(),
scripting_enabled: Cell::new(true),
animation_frame_ident: RefCell::new(0),
animation_frame_list: RefCell::new(HashMap::new()),
}
}

Expand Down
7 changes: 7 additions & 0 deletions components/script/dom/webidls/Window.webidl
Expand Up @@ -141,3 +141,10 @@ interface WindowLocalStorage {
readonly attribute Storage localStorage;
};
Window implements WindowLocalStorage;

// http://www.w3.org/TR/animation-timing/
partial interface Window {
long requestAnimationFrame(FrameRequestCallback callback);
void cancelAnimationFrame(long handle);
};
callback FrameRequestCallback = void (DOMHighResTimeStamp time);
25 changes: 22 additions & 3 deletions components/script/dom/window.rs
Expand Up @@ -3,17 +3,18 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::cell::DOMRefCell;
use dom::bindings::callback::ExceptionHandling;
use dom::bindings::codegen::Bindings::EventHandlerBinding::{OnErrorEventHandlerNonNull, EventHandlerNonNull};
use dom::bindings::codegen::Bindings::FunctionBinding::Function;
use dom::bindings::codegen::Bindings::WindowBinding;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::FunctionBinding::Function;
use dom::bindings::codegen::Bindings::WindowBinding::{self, WindowMethods, FrameRequestCallback};
use dom::bindings::codegen::InheritTypes::{NodeCast, EventTargetCast};
use dom::bindings::global::global_object_for_js_object;
use dom::bindings::error::{report_pending_exception, Fallible};
use dom::bindings::error::Error::InvalidCharacter;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{MutNullableJS, JSRef, Temporary, OptionalRootable, RootedReference};
use dom::bindings::num::Finite;
use dom::bindings::utils::{GlobalStaticData, Reflectable, WindowProxyHandler};
use dom::browsercontext::BrowserContext;
use dom::console::Console;
Expand Down Expand Up @@ -455,6 +456,23 @@ impl<'a> WindowMethods for JSRef<'a, Window> {
fn Atob(self, atob: DOMString) -> Fallible<DOMString> {
base64_atob(atob)
}

/// https://html.spec.whatwg.org/multipage/webappapis.html#dom-window-requestanimationframe
fn RequestAnimationFrame(self, callback: FrameRequestCallback) -> i32 {
let doc = self.Document().root();

let callback = move |now: f64| {
callback.Call__(Finite::wrap(now), ExceptionHandling::Rethrow);
};

doc.r().request_animation_frame(Box::new(callback))
}

/// https://html.spec.whatwg.org/multipage/webappapis.html#dom-window-cancelanimationframe
fn CancelAnimationFrame(self, ident: i32) {
let doc = self.Document().root();
doc.r().cancel_animation_frame(ident);
}
}

pub trait WindowHelpers {
Expand Down Expand Up @@ -865,6 +883,7 @@ impl Window {
session_storage: Default::default(),
local_storage: Default::default(),
timers: TimerManager::new(),

next_worker_id: Cell::new(WorkerId(0)),
id: id,
parent_info: parent_info,
Expand Down
1 change: 1 addition & 0 deletions components/script/lib.rs
Expand Up @@ -14,6 +14,7 @@
#![feature(std_misc)]
#![feature(unicode)]
#![feature(unsafe_destructor)]
#![feature(unboxed_closures)]

#![deny(unsafe_code)]
#![allow(non_snake_case)]
Expand Down

0 comments on commit d24c73a

Please sign in to comment.