Skip to content

Commit

Permalink
Implement basic Time To First Paint and First Contentful Paint PWMs
Browse files Browse the repository at this point in the history
  • Loading branch information
ferjm committed Jun 9, 2017
1 parent 2439672 commit 76336f4
Show file tree
Hide file tree
Showing 16 changed files with 195 additions and 6 deletions.
13 changes: 13 additions & 0 deletions Cargo.lock

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

6 changes: 6 additions & 0 deletions components/config/opts.rs
Expand Up @@ -219,6 +219,9 @@ pub struct Opts {

/// Unminify Javascript.
pub unminify_js: bool,

/// Print Progressive Web Metrics to console.
pub print_pwm: bool,
}

fn print_usage(app: &str, opts: &Options) {
Expand Down Expand Up @@ -537,6 +540,7 @@ pub fn default_opts() -> Opts {
signpost: false,
certificate_path: None,
unminify_js: false,
print_pwm: false,
}
}

Expand Down Expand Up @@ -598,6 +602,7 @@ pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult {
"config directory following xdg spec on linux platform", "");
opts.optflag("v", "version", "Display servo version information");
opts.optflag("", "unminify-js", "Unminify Javascript");
opts.optflag("", "print-pwm", "Print Progressive Web Metrics");

let opt_match = match opts.parse(args) {
Ok(m) => m,
Expand Down Expand Up @@ -832,6 +837,7 @@ pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult {
signpost: debug_options.signpost,
certificate_path: opt_match.opt_str("certificate-path"),
unminify_js: opt_match.opt_present("unminify-js"),
print_pwm: opt_match.opt_present("print-pwm"),
};

set_defaults(opts);
Expand Down
1 change: 1 addition & 0 deletions components/layout_thread/Cargo.toml
Expand Up @@ -21,6 +21,7 @@ layout = {path = "../layout"}
layout_traits = {path = "../layout_traits"}
lazy_static = "0.2"
log = "0.3.5"
metrics = {path = "../metrics"}
msg = {path = "../msg"}
net_traits = {path = "../net_traits"}
parking_lot = {version = "0.3.3", features = ["nightly"]}
Expand Down
19 changes: 18 additions & 1 deletion components/layout_thread/lib.rs
Expand Up @@ -22,6 +22,7 @@ extern crate layout_traits;
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate metrics;
extern crate msg;
extern crate net_traits;
extern crate parking_lot;
Expand Down Expand Up @@ -74,6 +75,7 @@ use layout::webrender_helpers::WebRenderDisplayListConverter;
use layout::wrapper::LayoutNodeLayoutData;
use layout::wrapper::drop_style_and_layout_data;
use layout_traits::LayoutThreadFactory;
use metrics::PaintTimeMetrics;
use msg::constellation_msg::PipelineId;
use msg::constellation_msg::TopLevelBrowsingContextId;
use net_traits::image_cache::{ImageCache, UsePlaceholder};
Expand Down Expand Up @@ -242,7 +244,10 @@ pub struct LayoutThread {
layout_threads: usize,

/// Which quirks mode are we rendering the document in?
quirks_mode: Option<QuirksMode>
quirks_mode: Option<QuirksMode>,

/// Paint time metrics.
paint_time_metrics: Option<Arc<PaintTimeMetrics>>,
}

impl LayoutThreadFactory for LayoutThread {
Expand Down Expand Up @@ -538,6 +543,7 @@ impl LayoutThread {
},
layout_threads: layout_threads,
quirks_mode: None,
paint_time_metrics: None,
}
}

Expand Down Expand Up @@ -704,6 +710,9 @@ impl LayoutThread {
debug_assert!(self.paint_worklet_executor.is_none());
self.paint_worklet_executor = Some(executor);
},
Msg::SetPaintTimeMetrics(paint_time_metrics) => {
self.paint_time_metrics = Some(paint_time_metrics.clone());
},
Msg::PrepareToExit(response_chan) => {
self.prepare_to_exit(response_chan);
return false
Expand Down Expand Up @@ -995,6 +1004,14 @@ impl LayoutThread {
self.epoch.set(epoch);

let viewport_size = webrender_traits::LayoutSize::from_untyped(&viewport_size);

// Set paint metrics if needed right before sending the display list to WebRender.
// XXX At some point, we may want to set this metric from WebRender itself.
if let Some(ref paint_time_metrics) = self.paint_time_metrics {
paint_time_metrics.maybe_set_first_paint(self.profiler_metadata());
paint_time_metrics.maybe_set_first_content_paint(self.profiler_metadata(), &display_list);
}

self.webrender_api.set_display_list(
Some(get_root_flow_background_color(layout_root)),
webrender_traits::Epoch(epoch.0),
Expand Down
16 changes: 16 additions & 0 deletions components/metrics/Cargo.toml
@@ -0,0 +1,16 @@
[package]
name = "metrics"
version = "0.0.1"
authors = ["The Servo Project Developers"]
license = "MPL-2.0"
publish = false

[lib]
name = "metrics"
path = "lib.rs"

[dependencies]
gfx = {path = "../gfx"}
profile_traits = {path = "../profile_traits"}
servo_config = {path = "../config"}
time = "0.1.12"
105 changes: 105 additions & 0 deletions components/metrics/lib.rs
@@ -0,0 +1,105 @@
/* 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/. */

extern crate gfx;
extern crate profile_traits;
extern crate servo_config;
extern crate time;

use gfx::display_list::{DisplayItem, DisplayList};
use profile_traits::time::{ProfilerChan, ProfilerCategory, send_profile_data};
use profile_traits::time::TimerMetadata;
use servo_config::opts;
use std::sync::{Arc, Mutex, RwLock};

macro_rules! make_time_setter(
( $attr:ident, $func:ident, $category:ident, $label:expr ) => (
fn $func(&self, profiler_metadata: Option<TimerMetadata>) {
let now = time::precise_time_ns() as f64;
let time = now - self.navigation_start;
{
let mut attr = self.$attr.write().unwrap();
*attr = Some(time);
}

// Send the metric to the time profiler.
{
let profiler_chan = self.time_profiler_chan.lock().unwrap();
send_profile_data(ProfilerCategory::$category,
profiler_metadata,
profiler_chan.clone(),
0, time as u64, 0, 0);
}

// Print the metric to console if the print-pwm option was given.
if opts::get().print_pwm {
println!("{:?} {:?}", $label, time);
}
}
);
);

pub struct PaintTimeMetrics {
navigation_start: f64,
first_paint: RwLock<Option<f64>>,
first_content_paint: RwLock<Option<f64>>,
time_profiler_chan: Arc<Mutex<ProfilerChan>>,
}

impl PaintTimeMetrics {
pub fn new(time_profiler_chan: ProfilerChan, navigation_start: f64)
-> PaintTimeMetrics {
PaintTimeMetrics {
navigation_start: navigation_start,
first_paint: RwLock::new(None),
first_content_paint: RwLock::new(None),
time_profiler_chan: Arc::new(Mutex::new(time_profiler_chan)),
}
}

make_time_setter!(first_paint, set_first_paint,
TimeToFirstPaint,
"first-paint");
make_time_setter!(first_content_paint, set_first_content_paint,
TimeToFirstContentfulPaint,
"first-contentful-paint");

pub fn maybe_set_first_paint(&self, profiler_metadata: Option<TimerMetadata>) {
{
let attr = self.first_paint.read().unwrap();
if attr.is_some() {
return;
}
}

self.set_first_paint(profiler_metadata);
}

pub fn maybe_set_first_content_paint(&self, profiler_metadata: Option<TimerMetadata>,
display_list: &DisplayList) {
{
let attr = self.first_content_paint.read().unwrap();
if attr.is_some() {
return;
}
}

// Analyze display list to figure out if this is the first contentful
// paint (i.e. the display list contains items of type text, image,
// non-white canvas or SVG)
// XXX Canvas and SVG
for item in &display_list.list {
match item {
&DisplayItem::Text(_) |
&DisplayItem::Image(_) => {
self.set_first_content_paint(profiler_metadata);
return;
},
_ => {
continue;
}
}
}
}
}
2 changes: 2 additions & 0 deletions components/profile/time.rs
Expand Up @@ -151,6 +151,8 @@ impl Formattable for ProfilerCategory {
ProfilerCategory::ScriptExitFullscreen => "Script Exit Fullscreen",
ProfilerCategory::ScriptWebVREvent => "Script WebVR Event",
ProfilerCategory::ScriptWorkletEvent => "Script Worklet Event",
ProfilerCategory::TimeToFirstPaint => "Time To First Paint",
ProfilerCategory::TimeToFirstContentfulPaint => "Time To First Contentful Paint",
ProfilerCategory::ApplicationHeartbeat => "Application Heartbeat",
};
format!("{}{}", padding, name)
Expand Down
2 changes: 2 additions & 0 deletions components/profile_traits/time.rs
Expand Up @@ -90,6 +90,8 @@ pub enum ProfilerCategory {
ScriptExitFullscreen = 0x78,
ScriptWebVREvent = 0x79,
ScriptWorkletEvent = 0x7a,
TimeToFirstPaint = 0x80,
TimeToFirstContentfulPaint = 0x81,
ApplicationHeartbeat = 0x90,
}

Expand Down
1 change: 1 addition & 0 deletions components/script/Cargo.toml
Expand Up @@ -58,6 +58,7 @@ jstraceable_derive = {path = "../jstraceable_derive"}
lazy_static = "0.2"
libc = "0.2"
log = "0.3.5"
metrics = {path = "../metrics"}
mime = "0.2.1"
mime_guess = "1.8.0"
msg = {path = "../msg"}
Expand Down
2 changes: 2 additions & 0 deletions components/script/dom/bindings/trace.rs
Expand Up @@ -59,6 +59,7 @@ use js::glue::{CallObjectTracer, CallValueTracer};
use js::jsapi::{GCTraceKindToAscii, Heap, JSObject, JSTracer, TraceKind};
use js::jsval::JSVal;
use js::rust::Runtime;
use metrics::PaintTimeMetrics;
use msg::constellation_msg::{BrowsingContextId, FrameType, PipelineId, TopLevelBrowsingContextId};
use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceThreads};
use net_traits::filemanager_thread::RelativePos;
Expand Down Expand Up @@ -392,6 +393,7 @@ unsafe_no_jsmanaged_fields!(WebGLTextureId);
unsafe_no_jsmanaged_fields!(WebGLVertexArrayId);
unsafe_no_jsmanaged_fields!(MediaList);
unsafe_no_jsmanaged_fields!(WebVRGamepadHand);
unsafe_no_jsmanaged_fields!(PaintTimeMetrics);

unsafe impl<'a> JSTraceable for &'a str {
#[inline]
Expand Down
18 changes: 14 additions & 4 deletions components/script/dom/window.rs
Expand Up @@ -62,6 +62,7 @@ use js::jsapi::{JS_GC, JS_GetRuntime};
use js::jsval::UndefinedValue;
use js::rust::Runtime;
use layout_image::fetch_image_for_layout;
use metrics::PaintTimeMetrics;
use msg::constellation_msg::{FrameType, PipelineId};
use net_traits::{ResourceThreads, ReferrerPolicy};
use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse};
Expand Down Expand Up @@ -183,6 +184,8 @@ pub struct Window {
history: MutNullableJS<History>,
custom_element_registry: MutNullableJS<CustomElementRegistry>,
performance: MutNullableJS<Performance>,
#[ignore_heap_size_of = "Arc"]
paint_time_metrics: Arc<PaintTimeMetrics>,
navigation_start: u64,
navigation_start_precise: f64,
screen: MutNullableJS<Screen>,
Expand Down Expand Up @@ -1773,6 +1776,10 @@ impl Window {
pub fn unminified_js_dir(&self) -> Option<String> {
self.unminified_js_dir.borrow().clone()
}

pub fn paint_time_metrics(&self) -> Arc<PaintTimeMetrics> {
self.paint_time_metrics.clone()
}
}

impl Window {
Expand Down Expand Up @@ -1812,13 +1819,15 @@ impl Window {
script_chan: Arc::new(Mutex::new(control_chan)),
};
let current_time = time::get_time();
let navigation_start_precise = time::precise_time_ns() as f64;

let win = box Window {
globalscope:
GlobalScope::new_inherited(
id,
devtools_chan,
devtools_chan.clone(),
mem_profiler_chan,
time_profiler_chan,
time_profiler_chan.clone(),
constellation_chan,
scheduler_chan,
resource_threads,
Expand All @@ -1838,8 +1847,10 @@ impl Window {
window_proxy: Default::default(),
document: Default::default(),
performance: Default::default(),
paint_time_metrics:
Arc::new(PaintTimeMetrics::new(time_profiler_chan, navigation_start_precise)),
navigation_start: (current_time.sec * 1000 + current_time.nsec as i64 / 1000000) as u64,
navigation_start_precise: time::precise_time_ns() as f64,
navigation_start_precise: navigation_start_precise,
screen: Default::default(),
session_storage: Default::default(),
local_storage: Default::default(),
Expand All @@ -1858,7 +1869,6 @@ impl Window {
suppress_reflow: Cell::new(true),
pending_reflow_count: Cell::new(0),
current_state: Cell::new(WindowState::Alive),

devtools_marker_sender: DOMRefCell::new(None),
devtools_markers: DOMRefCell::new(HashSet::new()),
webdriver_script_chan: DOMRefCell::new(None),
Expand Down
1 change: 1 addition & 0 deletions components/script/lib.rs
Expand Up @@ -67,6 +67,7 @@ extern crate lazy_static;
extern crate libc;
#[macro_use]
extern crate log;
extern crate metrics;
#[macro_use]
extern crate mime;
extern crate mime_guess;
Expand Down

0 comments on commit 76336f4

Please sign in to comment.