diff --git a/src/components/gfx/font.rs b/src/components/gfx/font.rs index 6405c0f34cb..3cc65061105 100644 --- a/src/components/gfx/font.rs +++ b/src/components/gfx/font.rs @@ -18,6 +18,8 @@ use azure::scaled_font::ScaledFont; use azure::azure_hl::{BackendType, ColorPattern}; use geom::{Point2D, Rect, Size2D}; +use servo_util::time::ProfilerChan; + // FontHandle encapsulates access to the platform's font API, // e.g. quartz, FreeType. It provides access to metrics and tables // needed by the text shaper as well as access to the underlying font @@ -210,13 +212,15 @@ pub struct Font { style: UsedFontStyle, metrics: FontMetrics, backend: BackendType, + profiler_chan: ProfilerChan, } pub impl Font { fn new_from_buffer(ctx: &FontContext, buffer: ~[u8], style: &SpecifiedFontStyle, - backend: BackendType) + backend: BackendType, + profiler_chan: ProfilerChan) -> Result<@mut Font, ()> { let handle = FontHandleMethods::new_from_buffer(&ctx.handle, buffer, style); let handle: FontHandle = if handle.is_ok() { @@ -235,11 +239,13 @@ pub impl Font { style: copy *style, metrics: metrics, backend: backend, + profiler_chan: profiler_chan, }); } fn new_from_adopted_handle(_fctx: &FontContext, handle: FontHandle, - style: &SpecifiedFontStyle, backend: BackendType) -> @mut Font { + style: &SpecifiedFontStyle, backend: BackendType, + profiler_chan: ProfilerChan) -> @mut Font { let metrics = handle.get_metrics(); @mut Font { @@ -249,11 +255,13 @@ pub impl Font { style: copy *style, metrics: metrics, backend: backend, + profiler_chan: profiler_chan, } } fn new_from_existing_handle(fctx: &FontContext, handle: &FontHandle, - style: &SpecifiedFontStyle, backend: BackendType) -> Result<@mut Font,()> { + style: &SpecifiedFontStyle, backend: BackendType, + profiler_chan: ProfilerChan) -> Result<@mut Font,()> { // TODO(Issue #179): convert between specified and used font style here? let styled_handle = match handle.clone_with_style(&fctx.handle, style) { @@ -261,7 +269,7 @@ pub impl Font { Err(()) => return Err(()) }; - return Ok(Font::new_from_adopted_handle(fctx, styled_handle, style, backend)); + return Ok(Font::new_from_adopted_handle(fctx, styled_handle, style, backend, profiler_chan)); } priv fn get_shaper(@mut self) -> @Shaper { diff --git a/src/components/gfx/font_context.rs b/src/components/gfx/font_context.rs index e954d3e07ed..e2d1abf4137 100644 --- a/src/components/gfx/font_context.rs +++ b/src/components/gfx/font_context.rs @@ -40,17 +40,18 @@ pub struct FontContext { handle: FontContextHandle, backend: BackendType, generic_fonts: HashMap<~str,~str>, + profiler_chan: ProfilerChan, } #[allow(non_implicitly_copyable_typarams)] pub impl<'self> FontContext { fn new(backend: BackendType, needs_font_list: bool, - prof_chan: ProfilerChan) + profiler_chan: ProfilerChan) -> FontContext { let handle = FontContextHandle::new(); let font_list = if needs_font_list { - Some(FontList::new(&handle, prof_chan.clone())) } + Some(FontList::new(&handle, profiler_chan.clone())) } else { None }; // TODO: Allow users to specify these. @@ -69,6 +70,7 @@ pub impl<'self> FontContext { handle: handle, backend: backend, generic_fonts: generic_fonts, + profiler_chan: profiler_chan, } } @@ -125,7 +127,8 @@ pub impl<'self> FontContext { for result.each |font_entry| { found = true; // TODO(Issue #203): route this instantion through FontContext's Font instance cache. - let instance = Font::new_from_existing_handle(self, &font_entry.handle, style, self.backend); + let instance = Font::new_from_existing_handle(self, &font_entry.handle, style, self.backend, + self.profiler_chan.clone()); do result::iter(&instance) |font: &@mut Font| { fonts.push(*font); } }; @@ -163,7 +166,8 @@ pub impl<'self> FontContext { Ok(Font::new_from_adopted_handle(self, handle, &desc.style, - self.backend)) + self.backend, + self.profiler_chan.clone())) }) } }; diff --git a/src/components/gfx/opts.rs b/src/components/gfx/opts.rs index d9c6ff1446c..d3193e2de7f 100644 --- a/src/components/gfx/opts.rs +++ b/src/components/gfx/opts.rs @@ -13,6 +13,7 @@ pub struct Opts { render_backend: BackendType, n_render_threads: uint, tile_size: uint, + profiler_period: Option, } #[allow(non_implicitly_copyable_typarams)] @@ -26,13 +27,13 @@ pub fn from_cmdline_args(args: &[~str]) -> Opts { getopts::optopt(~"r"), // rendering backend getopts::optopt(~"s"), // size of tiles getopts::optopt(~"t"), // threads to render with + getopts::optflagopt(~"p"), // profiler flag and output interval ]; let opt_match = match getopts::getopts(args, opts) { result::Ok(m) => { copy m } result::Err(f) => { fail!(getopts::fail_str(copy f)) } }; - let urls = if opt_match.free.is_empty() { fail!(~"servo asks that you provide 1 or more URLs") } else { @@ -68,10 +69,18 @@ pub fn from_cmdline_args(args: &[~str]) -> Opts { None => 1, // FIXME: Number of cores. }; + let profiler_period: Option = + // if only flag is present, default to 5 second period + match getopts::opt_default(&opt_match, ~"p", ~"5") { + Some(period) => Some(f64::from_str(period).get()), + None => None, + }; + Opts { urls: urls, render_backend: render_backend, n_render_threads: n_render_threads, tile_size: tile_size, + profiler_period: profiler_period, } } diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index 2beabee6641..457ae4aa5a4 100644 --- a/src/components/gfx/render_task.rs +++ b/src/components/gfx/render_task.rs @@ -18,9 +18,7 @@ use core::task::SingleThreaded; use std::task_pool::TaskPool; use servo_net::util::spawn_listener; -use servo_util::time::ProfilerChan; -use servo_util::time::profile; -use servo_util::time::time; +use servo_util::time::{ProfilerChan, profile}; use servo_util::time; pub enum Msg { @@ -124,7 +122,7 @@ impl Renderer { fn render(&mut self, render_layer: RenderLayer) { debug!("renderer: rendering"); - do time("rendering") { + do profile(time::RenderingCategory, self.profiler_chan.clone()) { let layer_buffer_set = do render_layers(&render_layer, &self.opts, self.profiler_chan.clone()) |render_layer_ref, diff --git a/src/components/gfx/text/text_run.rs b/src/components/gfx/text/text_run.rs index 3778e4353db..905371488dc 100644 --- a/src/components/gfx/text/text_run.rs +++ b/src/components/gfx/text/text_run.rs @@ -6,6 +6,8 @@ use font_context::FontContext; use geometry::Au; use text::glyph::{BreakTypeNormal, GlyphStore}; use font::{Font, FontDescriptor, RunMetrics}; +use servo_util::time; +use servo_util::time::profile; use servo_util::range::Range; /// A text run. @@ -44,7 +46,9 @@ pub impl<'self> TextRun { fn new(font: @mut Font, text: ~str, underline: bool) -> TextRun { let mut glyph_store = GlyphStore::new(str::char_len(text)); TextRun::compute_potential_breaks(text, &mut glyph_store); - font.shape_text(text, &mut glyph_store); + do profile(time::LayoutShapingCategory, font.profiler_chan.clone()) { + font.shape_text(text, &mut glyph_store); + } let run = TextRun { text: text, diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index c0b08be2f15..39d67aaf002 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -5,8 +5,10 @@ use compositing::resize_rate_limiter::ResizeRateLimiter; use platform::{Application, Window}; use script::script_task::{LoadMsg, ScriptMsg, SendEventMsg}; -use windowing::{ApplicationMethods, WindowMethods}; -use script::dom::event::ClickEvent; +use windowing::{ApplicationMethods, WindowMethods, WindowMouseEvent, WindowClickEvent}; +use windowing::{WindowMouseDownEvent, WindowMouseUpEvent}; + +use script::dom::event::{Event, ClickEvent, MouseDownEvent, MouseUpEvent}; use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods}; use core::cell::Cell; @@ -233,12 +235,24 @@ fn run_main_loop(port: Port, let script_chan_clone = script_chan.clone(); - // When the user clicks, perform hit testing - do window.set_click_callback |layer_click_point| { - let world_click_point = layer_click_point + *world_offset; - debug!("osmain: clicked at %?", world_click_point); - - script_chan_clone.send(SendEventMsg(ClickEvent(world_click_point))); + // When the user triggers a mouse event, perform appropriate hit testing + do window.set_mouse_callback |window_mouse_event: WindowMouseEvent| { + let event: Event; + let world_mouse_point = |layer_mouse_point: Point2D| { + layer_mouse_point + *world_offset + }; + match window_mouse_event { + WindowClickEvent(button, layer_mouse_point) => { + event = ClickEvent(button, world_mouse_point(layer_mouse_point)); + } + WindowMouseDownEvent(button, layer_mouse_point) => { + event = MouseDownEvent(button, world_mouse_point(layer_mouse_point)); + } + WindowMouseUpEvent(button, layer_mouse_point) => { + event = MouseUpEvent(button, world_mouse_point(layer_mouse_point)); + } + } + script_chan_clone.send(SendEventMsg(event)); } // When the user scrolls, move the layer around. diff --git a/src/components/main/css/select_handler.rs b/src/components/main/css/select_handler.rs index 56312ee210b..c04c48f688a 100644 --- a/src/components/main/css/select_handler.rs +++ b/src/components/main/css/select_handler.rs @@ -24,6 +24,16 @@ fn with_node_name(node: AbstractNode, f: &fn(&str) -> R) -> R { } impl SelectHandler> for NodeSelectHandler { + // FIXME(tkuehn): placeholder to get servo to compile + fn node_has_class(&self, node: &AbstractNode, s: &str) -> bool { + true + } + + // FIXME(tkuehn): placeholder to get servo to compile + fn with_node_classes(&self, node: &AbstractNode, f: &fn(Option<&str>) -> R) -> R { + f(None) + } + fn with_node_name(&self, node: &AbstractNode, f: &fn(&str) -> R) -> R { with_node_name(*node, f) } diff --git a/src/components/main/engine.rs b/src/components/main/engine.rs index 1d396c1a70f..6dd245214c8 100644 --- a/src/components/main/engine.rs +++ b/src/components/main/engine.rs @@ -18,7 +18,7 @@ use script::script_task; use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; use servo_net::resource_task::ResourceTask; use servo_net::resource_task; -use servo_util::time::{ProfilerChan, ProfilerPort, ProfilerTask}; +use servo_util::time::{profiler_force_print, ProfilerChan, ProfilerPort, ProfilerTask}; use std::net::url::Url; pub type EngineTask = Chan; @@ -39,6 +39,12 @@ pub struct Engine { profiler_task: ProfilerTask, } +impl Drop for Engine { + fn finalize(&self) { + profiler_force_print(self.profiler_task.chan.clone()); + } +} + impl Engine { pub fn start(compositor: CompositorTask, opts: &Opts, @@ -58,9 +64,12 @@ impl Engine { opts.with_ref(|o| copy *o), profiler_chan.clone()); - let profiler_task = ProfilerTask::new(profiler_port.take(), profiler_chan.clone()); - let opts = opts.take(); + + let profiler_task = ProfilerTask::new(profiler_port.take(), + profiler_chan.clone(), + opts.profiler_period); + let layout_task = layout_task::create_layout_task(render_task.clone(), image_cache_task.clone(), opts, diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 385014a1172..b96656abbd6 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -342,13 +342,14 @@ impl Layout { flow.build_display_list(&builder, &flow.position(), display_list); - // iterate in reverse to ensure we have the most recently painted render box let (x, y) = (Au::from_frac_px(point.x as float), Au::from_frac_px(point.y as float)); let mut resp = Err(()); let display_list = &display_list.take().list; + // iterate in reverse to ensure we have the most recently painted render box for display_list.each_reverse |display_item| { let bounds = display_item.bounds(); + // TODO this check should really be performed by a method of DisplayItem if x <= bounds.origin.x + bounds.size.width && bounds.origin.x <= x && y < bounds.origin.y + bounds.size.height && diff --git a/src/components/main/platform/common/glut_windowing.rs b/src/components/main/platform/common/glut_windowing.rs index 07a2f3bf9c2..0af1a0295d8 100644 --- a/src/components/main/platform/common/glut_windowing.rs +++ b/src/components/main/platform/common/glut_windowing.rs @@ -7,8 +7,9 @@ /// GLUT is a very old and bare-bones toolkit. However, it has good cross-platform support, at /// least on desktops. It is designed for testing Servo without the need of a UI. -use windowing::{ApplicationMethods, CompositeCallback, LoadUrlCallback, ClickCallback}; -use windowing::{ResizeCallback, ScrollCallback, WindowMethods}; +use windowing::{ApplicationMethods, CompositeCallback, LoadUrlCallback, MouseCallback}; +use windowing::{ResizeCallback, ScrollCallback, WindowMethods, WindowMouseEvent, WindowClickEvent}; +use windowing::{WindowMouseDownEvent, WindowMouseUpEvent}; use alert::{Alert, AlertMethods}; use core::libc::c_int; @@ -36,10 +37,12 @@ pub struct Window { composite_callback: Option, resize_callback: Option, load_url_callback: Option, - click_callback: Option, + mouse_callback: Option, scroll_callback: Option, drag_origin: Point2D, + mouse_down_button: @mut c_int, + mouse_down_point: @mut Point2D, } impl WindowMethods for Window { @@ -56,10 +59,12 @@ impl WindowMethods for Window { composite_callback: None, resize_callback: None, load_url_callback: None, - click_callback: None, + mouse_callback: None, scroll_callback: None, drag_origin: Point2D(0, 0), + mouse_down_button: @mut 0, + mouse_down_point: @mut Point2D(0, 0), }; // Register event handlers. @@ -79,9 +84,9 @@ impl WindowMethods for Window { do glut::keyboard_func |key, _, _| { window.handle_key(key) } - do glut::mouse_func |button, _, x, y| { + do glut::mouse_func |button, state, x, y| { if button < 3 { - window.handle_click(x, y); + window.handle_mouse(button, state, x, y); } else { window.handle_scroll(if button == 4 { -30.0 } else { 30.0 }); } @@ -117,9 +122,9 @@ impl WindowMethods for Window { self.load_url_callback = Some(new_load_url_callback) } - /// Registers a callback to be run when a click event occurs. - pub fn set_click_callback(&mut self, new_click_callback: ClickCallback) { - self.click_callback = Some(new_click_callback) + /// Registers a callback to be run when a mouse event occurs. + pub fn set_mouse_callback(&mut self, new_mouse_callback: MouseCallback) { + self.mouse_callback = Some(new_mouse_callback) } /// Registers a callback to be run when the user scrolls. @@ -148,10 +153,36 @@ impl Window { } /// Helper function to handle a click - fn handle_click(&self, x: c_int, y: c_int) { - match self.click_callback { + fn handle_mouse(&self, button: c_int, state: c_int, x: c_int, y: c_int) { + // FIXME(tkuehn): max pixel dist should be based on pixel density + let max_pixel_dist = 10f; + match self.mouse_callback { None => {} - Some(callback) => callback(Point2D(x as f32, y as f32)), + Some(callback) => { + let event: WindowMouseEvent; + match state { + glut::MOUSE_DOWN => { + event = WindowMouseDownEvent(button as uint, Point2D(x as f32, y as f32)); + *self.mouse_down_point = Point2D(x, y); + *self.mouse_down_button = button; + } + glut::MOUSE_UP => { + event = WindowMouseUpEvent(button as uint, Point2D(x as f32, y as f32)); + if *self.mouse_down_button == button { + let pixel_dist = *self.mouse_down_point - Point2D(x, y); + let pixel_dist = ((pixel_dist.x * pixel_dist.x + + pixel_dist.y * pixel_dist.y) as float).sqrt(); + if pixel_dist < max_pixel_dist { + let click_event = WindowClickEvent(button as uint, + Point2D(x as f32, y as f32)); + callback(click_event); + } + } + } + _ => fail!("I cannot recognize the type of mouse action that occured. :-(") + }; + callback(event); + } } } diff --git a/src/components/main/windowing.rs b/src/components/main/windowing.rs index 9a9cefaf03b..797f1445ceb 100644 --- a/src/components/main/windowing.rs +++ b/src/components/main/windowing.rs @@ -7,6 +7,12 @@ use geom::point::Point2D; use geom::size::Size2D; +pub enum WindowMouseEvent { + WindowClickEvent(uint, Point2D), + WindowMouseDownEvent(uint, Point2D), + WindowMouseUpEvent(uint, Point2D), +} + /// Type of the function that is called when the screen is to be redisplayed. pub type CompositeCallback = @fn(); @@ -16,9 +22,8 @@ pub type ResizeCallback = @fn(uint, uint); /// Type of the function that is called when a new URL is to be loaded. pub type LoadUrlCallback = @fn(&str); -/// Type of the function that is called when hit testing is to be performed. -/// FIXME this currently does not discriminate between left and right clicks or any modifiers -pub type ClickCallback = @fn(Point2D); +/// Type of the function that is called when a mouse hit test is to be performed. +pub type MouseCallback = @fn(WindowMouseEvent); /// Type of the function that is called when the user scrolls. pub type ScrollCallback = @fn(Point2D); @@ -43,7 +48,7 @@ pub trait WindowMethods { /// Registers a callback to run when a new URL is to be loaded. pub fn set_load_url_callback(&mut self, new_load_url_callback: LoadUrlCallback); /// Registers a callback to run when the user clicks. - pub fn set_click_callback(&mut self, new_click_callback: ClickCallback); + pub fn set_mouse_callback(&mut self, new_mouse_callback: MouseCallback); /// Registers a callback to run when the user scrolls. pub fn set_scroll_callback(&mut self, new_scroll_callback: ScrollCallback); diff --git a/src/components/script/dom/event.rs b/src/components/script/dom/event.rs index a0ef39d8fca..6c11bcd3411 100644 --- a/src/components/script/dom/event.rs +++ b/src/components/script/dom/event.rs @@ -12,7 +12,9 @@ use geom::point::Point2D; pub enum Event { ResizeEvent(uint, uint, comm::Chan<()>), ReflowEvent, - ClickEvent(Point2D), + ClickEvent(uint, Point2D), + MouseDownEvent(uint, Point2D), + MouseUpEvent(uint, Point2D), } pub struct Event_ { diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs index 66a4e9e4727..69a588667c2 100644 --- a/src/components/script/script_task.rs +++ b/src/components/script/script_task.rs @@ -7,7 +7,8 @@ use dom::bindings::utils::GlobalStaticData; use dom::document::Document; -use dom::event::{Event, ResizeEvent, ReflowEvent, ClickEvent}; +use dom::element::Element; +use dom::event::{Event, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent, MouseUpEvent}; use dom::node::{AbstractNode, ScriptView, define_bindings}; use dom::window::Window; use layout_interface::{AddStylesheetMsg, DocumentDamage, DocumentDamageLevel, HitTestQuery}; @@ -37,7 +38,7 @@ use js; use servo_net::image_cache_task::ImageCacheTask; use servo_net::resource_task::ResourceTask; use servo_util::tree::TreeNodeRef; -use std::net::url::Url; +use std::net::url::{Url, from_str}; use std::net::url; /// Messages used to control the script task. @@ -503,7 +504,7 @@ impl ScriptContext { } } - ClickEvent(point) => { + ClickEvent(button, point) => { debug!("ClickEvent: clicked at %?", point); let root = match self.root_frame { Some(ref frame) => frame.document.root, @@ -511,14 +512,51 @@ impl ScriptContext { }; match self.query_layout(HitTestQuery(root, point)) { Ok(node) => match node { - HitTestResponse(node) => debug!("clicked on %?", node.debug_str()), + HitTestResponse(node) => { + debug!("clicked on %?", node.debug_str()); + let mut node = node; + // traverse node generations until a node that is an element is found + while !node.is_element() { + match node.parent_node() { + Some(parent) => { + node = parent; + } + None => break + } + } + if node.is_element() { + do node.with_imm_element |element| { + match element.tag_name { + ~"a" => self.load_url_from_element(element), + _ => {} + } + } + } + } _ => fail!(~"unexpected layout reply") }, Err(()) => { - println(fmt!("layout query error")); + debug!(fmt!("layout query error")); } }; } + MouseDownEvent(*) => {} + MouseUpEvent(*) => {} + } + } + + priv fn load_url_from_element(&self, element: &Element) { + // if the node's element is "a," load url from href attr + for element.attrs.each |attr| { + if attr.name == ~"href" { + debug!("clicked on link to %?", attr.value); + let url = from_str(attr.value); + match url { + Ok(url) => self.script_chan.send(LoadMsg(url)), + Err(msg) => debug!(msg) + }; + break; + } } } } diff --git a/src/components/util/time.rs b/src/components/util/time.rs index 8bb698981f0..8d2aceb8abd 100644 --- a/src/components/util/time.rs +++ b/src/components/util/time.rs @@ -6,62 +6,129 @@ use std::time::precise_time_ns; use core::cell::Cell; use core::comm::{Port, SharedChan}; -use core::os::getenv; +use std::sort::tim_sort; +#[deriving(Eq)] pub enum ProfilerCategory { CompositingCategory, - LayoutPerformCategory, LayoutQueryCategory, + LayoutPerformCategory, LayoutAuxInitCategory, LayoutSelectorMatchCategory, LayoutTreeBuilderCategory, LayoutMainCategory, + LayoutShapingCategory, LayoutDispListBuildCategory, GfxRegenAvailableFontsCategory, RenderingPrepBuffCategory, RenderingWaitSubtasksCategory, RenderingCategory, + // hackish but helps prevent errors when adding new categories + NUM_BUCKETS, } -// change this whenever buckets are added/rm'd -static NUM_BUCKETS: uint = 12; -pub type ProfilerChan = SharedChan<(ProfilerCategory, uint)>; -pub type ProfilerPort = Port<(ProfilerCategory, uint)>; +impl ProfilerCategory { + + // convenience function to not have to cast every time + pub fn num_buckets() -> uint { + NUM_BUCKETS as uint + } + + // enumeration of all ProfilerCategory types + // FIXME(tkuehn): this is ugly and error-prone, + // but currently we lack better alternatives without an enum enumeration + priv fn empty_buckets() -> ~[(ProfilerCategory, ~[f64])] { + let mut vec = ~[]; + vec.push((CompositingCategory, ~[])); + vec.push((LayoutQueryCategory, ~[])); + vec.push((LayoutPerformCategory, ~[])); + vec.push((LayoutAuxInitCategory, ~[])); + vec.push((LayoutSelectorMatchCategory, ~[])); + vec.push((LayoutTreeBuilderCategory, ~[])); + vec.push((LayoutMainCategory, ~[])); + vec.push((LayoutShapingCategory, ~[])); + vec.push((LayoutDispListBuildCategory, ~[])); + vec.push((GfxRegenAvailableFontsCategory, ~[])); + vec.push((RenderingPrepBuffCategory, ~[])); + vec.push((RenderingWaitSubtasksCategory, ~[])); + vec.push((RenderingCategory, ~[])); + + ProfilerCategory::check_order(vec); + vec + } + + priv fn check_order(vec: &[(ProfilerCategory, ~[f64])]) { + for vec.each |&(category, _)| { + if category != vec[category as uint].first() { + fail!("Enum category does not match bucket index. This is a bug."); + } + } + } + + // some categories are subcategories of LayoutPerformCategory + // and should be printed to indicate this + pub fn format(self) -> ~str { + let padding = match self { + LayoutAuxInitCategory | LayoutSelectorMatchCategory | LayoutTreeBuilderCategory | + LayoutMainCategory | LayoutDispListBuildCategory | LayoutShapingCategory=> " - ", + _ => "" + }; + fmt!("%s%?", padding, self) + } +} +// FIXME(#5873) this should be initialized by a NUM_BUCKETS cast, +static BUCKETS: uint = 13; + +pub enum ProfilerMsg { + // Normal message used for reporting time + TimeMsg(ProfilerCategory, f64), + // Message used to force print the profiling metrics + ForcePrintMsg, +} +pub type ProfilerChan = SharedChan; +pub type ProfilerPort = Port; pub struct ProfilerTask { chan: ProfilerChan, } impl ProfilerTask { - pub fn new(prof_port: ProfilerPort, - prof_chan: ProfilerChan) + pub fn new(profiler_port: ProfilerPort, + profiler_chan: ProfilerChan, + period: Option) -> ProfilerTask { - let prof_port = Cell(prof_port); + let profiler_port = Cell(profiler_port); do spawn { - let mut profiler_context = ProfilerContext::new(prof_port.take()); + let mut profiler_context = ProfilerContext::new(profiler_port.take(), period); profiler_context.start(); } ProfilerTask { - chan: prof_chan + chan: profiler_chan } } } pub struct ProfilerContext { port: ProfilerPort, - buckets: [~[uint], ..NUM_BUCKETS], - verbose: Option<~str>, - mut last_print: u64, + buckets: ~[(ProfilerCategory, ~[f64])], + verbose: bool, + period: f64, + mut last_print: f64, } impl ProfilerContext { - pub fn new(port: ProfilerPort) -> ProfilerContext { + pub fn new(port: ProfilerPort, period: Option) -> ProfilerContext { + let (verbose, period) = match period { + Some(period) => (true, period), + None => (false, 0f64) + }; ProfilerContext { port: port, - buckets: [~[], ..NUM_BUCKETS], - verbose: getenv("SERVO_PROFILER"), - last_print: 0, + buckets: ProfilerCategory::empty_buckets(), + verbose: verbose, + period: period, + last_print: 0f64, } } @@ -72,62 +139,76 @@ impl ProfilerContext { } } - priv fn handle_msg(&mut self, msg: (ProfilerCategory, uint)) { - let (prof_msg, t) = msg; - self.buckets[prof_msg as uint].push(t); - if self.verbose.is_some() { - let cur_time = precise_time_ns() / 1000000000u64; - if cur_time - self.last_print > 5 { - self.last_print = cur_time; - let mut i = 0; - for self.buckets.each |bucket| { - let prof_msg = match i { - // must be in same order as ProfilerCategory - 0 => CompositingCategory, - 1 => LayoutPerformCategory, - 2 => LayoutQueryCategory, - 3 => LayoutAuxInitCategory, - 4 => LayoutSelectorMatchCategory, - 5 => LayoutTreeBuilderCategory, - 6 => LayoutMainCategory, - 7 => LayoutDispListBuildCategory, - 8 => GfxRegenAvailableFontsCategory, - 9 => RenderingPrepBuffCategory, - 10 => RenderingWaitSubtasksCategory, - 11 => RenderingCategory, - _ => fail!() - }; - io::println(fmt!("%?: %f", prof_msg, - (bucket.foldl(0 as uint, |a, b| a + *b) as float) / - (bucket.len() as float))); - i += 1; + priv fn handle_msg(&mut self, msg: ProfilerMsg) { + match msg { + TimeMsg(category, t) => { + // FIXME(#3874): this should be a let (cat, ref mut bucket) = ..., + // not a match + match self.buckets[category as uint] { + (_, ref mut data) => { + data.push(t); + tim_sort(*data); + } } - io::println(""); + + if self.verbose { + let cur_time = precise_time_ns() as f64 / 1000000000f64; + if cur_time - self.last_print > self.period { + self.last_print = cur_time; + self.print_buckets(); + } + } + } + ForcePrintMsg => self.print_buckets(), + }; + } + + priv fn print_buckets(&mut self) { + println(fmt!("%31s %15s %15s %15s %15s %15s", + "_category (ms)_", "_mean (ms)_", "_median (ms)_", + "_min (ms)_", "_max (ms)_", "_bucket size_")); + for self.buckets.each |bucket| { + let &(category, data) = bucket; + let data_len = data.len(); + if data_len > 0 { + let (mean, median, min, max) = + (data.foldl(0f64, |a, b| a + *b) / (data_len as f64), + data[data_len / 2], + data.min(), + data.max()); + println(fmt!("%-30s: %15.4? %15.4? %15.4? %15.4? %15u", + category.format(), mean, median, min, max, data_len)); } } - + println(""); } } -pub fn profile(cat: ProfilerCategory, - prof_chan: ProfilerChan, + +pub fn profile(category: ProfilerCategory, + profiler_chan: ProfilerChan, callback: &fn() -> T) -> T { let start_time = precise_time_ns(); let val = callback(); let end_time = precise_time_ns(); - let ms = ((end_time - start_time) / 1000000u64) as uint; - prof_chan.send((cat, ms)); + let ms = ((end_time - start_time) as f64 / 1000000f64); + profiler_chan.send(TimeMsg(category, ms)); return val; } + +pub fn profiler_force_print(profiler_chan: ProfilerChan) { + profiler_chan.send(ForcePrintMsg); +} + pub fn time(msg: &str, callback: &fn() -> T) -> T{ let start_time = precise_time_ns(); let val = callback(); let end_time = precise_time_ns(); - let ms = ((end_time - start_time) / 1000000u64) as uint; - if ms >= 5 { - debug!("%s took %u ms", msg, ms); + let ms = ((end_time - start_time) as f64 / 1000000f64); + if ms >= 5f64 { + debug!("%s took %? ms", msg, ms); } return val; } diff --git a/src/support/css/rust-css b/src/support/css/rust-css index 865f5391143..09d2db847c1 160000 --- a/src/support/css/rust-css +++ b/src/support/css/rust-css @@ -1 +1 @@ -Subproject commit 865f539114383a021822583801e8362faf916699 +Subproject commit 09d2db847c11bcab7f1832d5daf5947a7c1384ee diff --git a/src/support/glut/rust-glut b/src/support/glut/rust-glut index 453bf81e021..7db24a19d25 160000 --- a/src/support/glut/rust-glut +++ b/src/support/glut/rust-glut @@ -1 +1 @@ -Subproject commit 453bf81e021008f5eba29b135f07f4529e6c8b2e +Subproject commit 7db24a19d25fbedca2898381ae0b13b723c14135 diff --git a/src/support/netsurfcss/rust-netsurfcss b/src/support/netsurfcss/rust-netsurfcss index 325cd5197ed..8fc7400ed33 160000 --- a/src/support/netsurfcss/rust-netsurfcss +++ b/src/support/netsurfcss/rust-netsurfcss @@ -1 +1 @@ -Subproject commit 325cd5197ed953f5c7c9317111b20ec1599eaffe +Subproject commit 8fc7400ed332c3d9edf358c2a18dd047ebde09a6 diff --git a/src/test/html/test_slam_layout.js b/src/test/html/test_slam_layout.js index 5d1726b904e..b42fba50641 100644 --- a/src/test/html/test_slam_layout.js +++ b/src/test/html/test_slam_layout.js @@ -12,3 +12,7 @@ for (var i = 0; i < count; i++) { } var stop = new Date(); window.alert((stop - start) / count * 1e6 + " ns/layout"); +<<<<<<< HEAD +======= +window.close(); +>>>>>>> 0560988... Add link following and refactor the profiler.