diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2016-02-19 00:54:06 +0530 |
---|---|---|
committer | bors-servo <lbergstrom+bors@mozilla.com> | 2016-02-19 00:54:06 +0530 |
commit | ab07b06823ea9748a6091aee2281495f86f00bce (patch) | |
tree | 2dd06ac4834d5bc4bcba6e4ada3aab12bb2c12d9 /components | |
parent | fe70efe07f6d72665f10c752884e5705d5bdc600 (diff) | |
parent | c0531c312fdb0783e4d121b4c2d7f15d4f5cdc1f (diff) | |
download | servo-ab07b06823ea9748a6091aee2281495f86f00bce.tar.gz servo-ab07b06823ea9748a6091aee2281495f86f00bce.zip |
Auto merge of #9589 - glennw:webrender, r=pcwalton
Add WebRender integration to Servo.
WebRender is an experimental GPU accelerated rendering backend for Servo.
The WebRender backend can be specified by running Servo with the -w option (otherwise the default rendering backend will be used).
WebRender has many bugs, and missing features - but it is usable to browse most websites - please report any WebRender specific rendering bugs you encounter!
<!-- Reviewable:start -->
[<img src="https://reviewable.io/review_button.svg" height="40" alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/9589)
<!-- Reviewable:end -->
Diffstat (limited to 'components')
41 files changed, 1513 insertions, 831 deletions
diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index 96f7a3c0777..34451e67931 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -33,6 +33,9 @@ git = "https://github.com/ecoal95/rust-offscreen-rendering-context" [dependencies.ipc-channel] git = "https://github.com/servo/ipc-channel" +[dependencies.webrender_traits] +git = "https://github.com/glennw/webrender_traits" + [dependencies] log = "0.3" num = "0.1.24" diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs index 92b3863cdf9..1bcf294d3b6 100644 --- a/components/canvas/canvas_paint_thread.rs +++ b/components/canvas/canvas_paint_thread.rs @@ -24,6 +24,7 @@ use std::sync::mpsc::{Sender, channel}; use util::opts; use util::thread::spawn_named; use util::vec::byte_swap; +use webrender_traits; impl<'a> CanvasPaintThread<'a> { /// It reads image data from the canvas @@ -63,6 +64,8 @@ pub struct CanvasPaintThread<'a> { path_builder: PathBuilder, state: CanvasPaintState<'a>, saved_states: Vec<CanvasPaintState<'a>>, + webrender_api: Option<webrender_traits::RenderApi>, + webrender_image_key: Option<webrender_traits::ImageKey>, } #[derive(Clone)] @@ -102,27 +105,34 @@ impl<'a> CanvasPaintState<'a> { } impl<'a> CanvasPaintThread<'a> { - fn new(size: Size2D<i32>) -> CanvasPaintThread<'a> { + fn new(size: Size2D<i32>, + webrender_api_sender: Option<webrender_traits::RenderApiSender>) -> CanvasPaintThread<'a> { let draw_target = CanvasPaintThread::create(size); let path_builder = draw_target.create_path_builder(); + let webrender_api = webrender_api_sender.map(|wr| wr.create_api()); + let webrender_image_key = webrender_api.as_ref().map(|wr| wr.alloc_image()); CanvasPaintThread { drawtarget: draw_target, path_builder: path_builder, state: CanvasPaintState::new(), saved_states: Vec::new(), + webrender_api: webrender_api, + webrender_image_key: webrender_image_key, } } /// Creates a new `CanvasPaintThread` and returns the out-of-process sender and the in-process /// sender for it. - pub fn start(size: Size2D<i32>) -> (IpcSender<CanvasMsg>, Sender<CanvasMsg>) { + pub fn start(size: Size2D<i32>, + webrender_api_sender: Option<webrender_traits::RenderApiSender>) + -> (IpcSender<CanvasMsg>, Sender<CanvasMsg>) { // TODO(pcwalton): Ask the pipeline to create this for us instead of spawning it directly. // This will be needed for multiprocess Servo. let (out_of_process_chan, out_of_process_port) = ipc::channel::<CanvasMsg>().unwrap(); let (in_process_chan, in_process_port) = channel(); ROUTER.route_ipc_receiver_to_mpsc_sender(out_of_process_port, in_process_chan.clone()); spawn_named("CanvasThread".to_owned(), move || { - let mut painter = CanvasPaintThread::new(size); + let mut painter = CanvasPaintThread::new(size, webrender_api_sender); loop { let msg = in_process_port.recv(); match msg.unwrap() { @@ -190,8 +200,8 @@ impl<'a> CanvasPaintThread<'a> { }, CanvasMsg::FromLayout(message) => { match message { - FromLayoutMsg::SendPixelContents(chan) => { - painter.send_pixel_contents(chan) + FromLayoutMsg::SendData(chan) => { + painter.send_data(chan) } } } @@ -519,9 +529,24 @@ impl<'a> CanvasPaintThread<'a> { self.drawtarget = CanvasPaintThread::create(size); } - fn send_pixel_contents(&mut self, chan: IpcSender<IpcSharedMemory>) { + fn send_data(&mut self, chan: IpcSender<CanvasData>) { self.drawtarget.snapshot().get_data_surface().with_data(|element| { - chan.send(IpcSharedMemory::from_bytes(element)).unwrap(); + if let Some(ref webrender_api) = self.webrender_api { + let size = self.drawtarget.get_size(); + let mut bytes = Vec::new(); + bytes.extend_from_slice(element); + webrender_api.update_image(self.webrender_image_key.unwrap(), + size.width as u32, + size.height as u32, + webrender_traits::ImageFormat::RGBA8, + bytes); + } + + let pixel_data = CanvasPixelData { + image_data: IpcSharedMemory::from_bytes(element), + image_key: self.webrender_image_key, + }; + chan.send(CanvasData::Pixels(pixel_data)).unwrap(); }) } diff --git a/components/canvas/lib.rs b/components/canvas/lib.rs index 7377e0385c7..5ee82053005 100644 --- a/components/canvas/lib.rs +++ b/components/canvas/lib.rs @@ -2,7 +2,6 @@ * 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/. */ -#![feature(nonzero)] #![feature(plugin)] #![plugin(plugins)] @@ -19,6 +18,7 @@ extern crate log; extern crate num; extern crate offscreen_gl_context; extern crate util; +extern crate webrender_traits; pub mod canvas_paint_thread; mod premultiplytable; diff --git a/components/canvas/webgl_paint_thread.rs b/components/canvas/webgl_paint_thread.rs index 913ef1b33ce..271ba0e2052 100644 --- a/components/canvas/webgl_paint_thread.rs +++ b/components/canvas/webgl_paint_thread.rs @@ -2,9 +2,8 @@ * 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 canvas_traits::{CanvasCommonMsg, CanvasMsg, CanvasWebGLMsg, FromLayoutMsg, FromPaintMsg}; -use canvas_traits::{WebGLError, WebGLFramebufferBindingRequest, WebGLParameter, WebGLResult}; -use core::nonzero::NonZero; +use canvas_traits::{CanvasCommonMsg, CanvasMsg, CanvasPixelData, CanvasData, CanvasWebGLMsg}; +use canvas_traits::{FromLayoutMsg, FromPaintMsg}; use euclid::size::Size2D; use gleam::gl; use ipc_channel::ipc::{self, IpcSender, IpcSharedMemory}; @@ -15,186 +14,59 @@ use std::borrow::ToOwned; use std::sync::mpsc::{Sender, channel}; use util::thread::spawn_named; use util::vec::byte_swap; +use webrender_traits; + +enum WebGLPaintTaskData { + WebRender(webrender_traits::RenderApi, webrender_traits::WebGLContextId), + Servo(GLContext<NativeGLContext>), +} pub struct WebGLPaintThread { size: Size2D<i32>, - original_context_size: Size2D<i32>, - gl_context: GLContext<NativeGLContext>, + data: WebGLPaintTaskData, } impl WebGLPaintThread { - fn new(size: Size2D<i32>, attrs: GLContextAttributes) -> Result<WebGLPaintThread, &'static str> { - let context = try!(GLContext::new(size, attrs, ColorAttachmentType::Texture, None)); - - // NOTE: As of right now this is always equal to the size parameter, - // but this doesn't have to be true. Firefox after failing with - // the requested size, tries with the nearest powers of two, for example. - let real_size = context.borrow_draw_buffer().unwrap().size(); + fn new(size: Size2D<i32>, + attrs: GLContextAttributes, + webrender_api_sender: Option<webrender_traits::RenderApiSender>) -> Result<WebGLPaintThread, String> { + let data = if let Some(sender) = webrender_api_sender { + let webrender_api = sender.create_api(); + let id = try!(webrender_api.request_webgl_context(&size, attrs)); + WebGLPaintTaskData::WebRender(webrender_api, id) + } else { + let context = try!(GLContext::<NativeGLContext>::new(size, attrs, ColorAttachmentType::Texture, None)); + WebGLPaintTaskData::Servo(context) + }; Ok(WebGLPaintThread { - size: real_size, - original_context_size: real_size, - gl_context: context + size: size, + data: data, }) } - /// In this function the gl commands are called. - /// Those messages that just involve a gl call have the call inlined, - /// processing of messages that require extra work are moved to functions - /// - /// NB: Not gl-related validations (names, lengths, accepted parameters...) are - /// done in the corresponding DOM interfaces pub fn handle_webgl_message(&self, message: CanvasWebGLMsg) { debug!("WebGL message: {:?}", message); - - match message { - CanvasWebGLMsg::GetContextAttributes(sender) => - self.context_attributes(sender), - CanvasWebGLMsg::ActiveTexture(target) => - gl::active_texture(target), - CanvasWebGLMsg::AttachShader(program_id, shader_id) => - gl::attach_shader(program_id, shader_id), - CanvasWebGLMsg::BindAttribLocation(program_id, index, name) => - gl::bind_attrib_location(program_id, index, &name), - CanvasWebGLMsg::BlendColor(r, g, b, a) => - gl::blend_color(r, g, b, a), - CanvasWebGLMsg::BlendEquation(mode) => - gl::blend_equation(mode), - CanvasWebGLMsg::BlendEquationSeparate(mode_rgb, mode_alpha) => - gl::blend_equation_separate(mode_rgb, mode_alpha), - CanvasWebGLMsg::BlendFunc(src, dest) => - gl::blend_func(src, dest), - CanvasWebGLMsg::BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha) => - gl::blend_func_separate(src_rgb, dest_rgb, src_alpha, dest_alpha), - CanvasWebGLMsg::BufferData(buffer_type, data, usage) => - gl::buffer_data(buffer_type, &data, usage), - CanvasWebGLMsg::BufferSubData(buffer_type, offset, data) => - gl::buffer_sub_data(buffer_type, offset, &data), - CanvasWebGLMsg::Clear(mask) => - gl::clear(mask), - CanvasWebGLMsg::ClearColor(r, g, b, a) => - gl::clear_color(r, g, b, a), - CanvasWebGLMsg::ClearDepth(depth) => - gl::clear_depth(depth), - CanvasWebGLMsg::ClearStencil(stencil) => - gl::clear_stencil(stencil), - CanvasWebGLMsg::ColorMask(r, g, b, a) => - gl::color_mask(r, g, b, a), - CanvasWebGLMsg::CullFace(mode) => - gl::cull_face(mode), - CanvasWebGLMsg::DepthFunc(func) => - gl::depth_func(func), - CanvasWebGLMsg::DepthMask(flag) => - gl::depth_mask(flag), - CanvasWebGLMsg::DepthRange(near, far) => - gl::depth_range(near, far), - CanvasWebGLMsg::Disable(cap) => - gl::disable(cap), - CanvasWebGLMsg::Enable(cap) => - gl::enable(cap), - CanvasWebGLMsg::FrontFace(mode) => - gl::front_face(mode), - CanvasWebGLMsg::DrawArrays(mode, first, count) => - gl::draw_arrays(mode, first, count), - CanvasWebGLMsg::DrawElements(mode, count, type_, offset) => - gl::draw_elements(mode, count, type_, offset as u32), - CanvasWebGLMsg::Hint(name, val) => - gl::hint(name, val), - CanvasWebGLMsg::LineWidth(width) => - gl::line_width(width), - CanvasWebGLMsg::PixelStorei(name, val) => - gl::pixel_store_i(name, val), - CanvasWebGLMsg::PolygonOffset(factor, units) => - gl::polygon_offset(factor, units), - CanvasWebGLMsg::Scissor(x, y, width, height) => - gl::scissor(x, y, width, height), - CanvasWebGLMsg::EnableVertexAttribArray(attrib_id) => - gl::enable_vertex_attrib_array(attrib_id), - CanvasWebGLMsg::GetAttribLocation(program_id, name, chan) => - self.attrib_location(program_id, name, chan), - CanvasWebGLMsg::GetBufferParameter(target, param_id, chan) => - self.buffer_parameter(target, param_id, chan), - CanvasWebGLMsg::GetParameter(param_id, chan) => - self.parameter(param_id, chan), - CanvasWebGLMsg::GetProgramParameter(program_id, param_id, chan) => - self.program_parameter(program_id, param_id, chan), - CanvasWebGLMsg::GetShaderParameter(shader_id, param_id, chan) => - self.shader_parameter(shader_id, param_id, chan), - CanvasWebGLMsg::GetUniformLocation(program_id, name, chan) => - self.uniform_location(program_id, name, chan), - CanvasWebGLMsg::CompileShader(shader_id, source) => - self.compile_shader(shader_id, source), - CanvasWebGLMsg::CreateBuffer(chan) => - self.create_buffer(chan), - CanvasWebGLMsg::CreateFramebuffer(chan) => - self.create_framebuffer(chan), - CanvasWebGLMsg::CreateRenderbuffer(chan) => - self.create_renderbuffer(chan), - CanvasWebGLMsg::CreateTexture(chan) => - self.create_texture(chan), - CanvasWebGLMsg::CreateProgram(chan) => - self.create_program(chan), - CanvasWebGLMsg::CreateShader(shader_type, chan) => - self.create_shader(shader_type, chan), - CanvasWebGLMsg::DeleteBuffer(id) => - gl::delete_buffers(&[id]), - CanvasWebGLMsg::DeleteFramebuffer(id) => - gl::delete_framebuffers(&[id]), - CanvasWebGLMsg::DeleteRenderbuffer(id) => - gl::delete_renderbuffers(&[id]), - CanvasWebGLMsg::DeleteTexture(id) => - gl::delete_textures(&[id]), - CanvasWebGLMsg::DeleteProgram(id) => - gl::delete_program(id), - CanvasWebGLMsg::DeleteShader(id) => - gl::delete_shader(id), - CanvasWebGLMsg::BindBuffer(target, id) => - gl::bind_buffer(target, id), - CanvasWebGLMsg::BindFramebuffer(target, request) => - self.bind_framebuffer(target, request), - CanvasWebGLMsg::BindRenderbuffer(target, id) => - gl::bind_renderbuffer(target, id), - CanvasWebGLMsg::BindTexture(target, id) => - gl::bind_texture(target, id), - CanvasWebGLMsg::LinkProgram(program_id) => - gl::link_program(program_id), - CanvasWebGLMsg::Uniform1f(uniform_id, x) => - gl::uniform_1f(uniform_id, x), - CanvasWebGLMsg::Uniform4f(uniform_id, x, y, z, w) => - gl::uniform_4f(uniform_id, x, y, z, w), - CanvasWebGLMsg::UseProgram(program_id) => - gl::use_program(program_id), - CanvasWebGLMsg::VertexAttrib(attrib_id, x, y, z, w) => - gl::vertex_attrib_4f(attrib_id, x, y, z, w), - CanvasWebGLMsg::VertexAttribPointer2f(attrib_id, size, normalized, stride, offset) => - gl::vertex_attrib_pointer_f32(attrib_id, size, normalized, stride, offset as u32), - CanvasWebGLMsg::Viewport(x, y, width, height) => - gl::viewport(x, y, width, height), - CanvasWebGLMsg::TexImage2D(target, level, internal, width, height, format, data_type, data) => - gl::tex_image_2d(target, level, internal, width, height, /*border*/0, format, data_type, Some(&data)), - CanvasWebGLMsg::TexParameteri(target, name, value) => - gl::tex_parameter_i(target, name, value), - CanvasWebGLMsg::TexParameterf(target, name, value) => - gl::tex_parameter_f(target, name, value), - CanvasWebGLMsg::DrawingBufferWidth(sender) => - self.send_drawing_buffer_width(sender), - CanvasWebGLMsg::DrawingBufferHeight(sender) => - self.send_drawing_buffer_height(sender), + match self.data { + WebGLPaintTaskData::WebRender(ref api, id) => { + api.send_webgl_command(id, message); + } + WebGLPaintTaskData::Servo(ref ctx) => { + message.apply(ctx); + } } - - // FIXME: Use debug_assertions once tests are run with them - let error = gl::get_error(); - assert!(error == gl::NO_ERROR, "Unexpected WebGL error: 0x{:x} ({})", error, error); } /// Creates a new `WebGLPaintThread` and returns the out-of-process sender and the in-process /// sender for it. - pub fn start(size: Size2D<i32>, attrs: GLContextAttributes) - -> Result<(IpcSender<CanvasMsg>, Sender<CanvasMsg>), &'static str> { + pub fn start(size: Size2D<i32>, + attrs: GLContextAttributes, + webrender_api_sender: Option<webrender_traits::RenderApiSender>) + -> Result<(IpcSender<CanvasMsg>, Sender<CanvasMsg>), String> { let (in_process_chan, in_process_port) = channel(); let (result_chan, result_port) = channel(); spawn_named("WebGLThread".to_owned(), move || { - let mut painter = match WebGLPaintThread::new(size, attrs) { + let mut painter = match WebGLPaintThread::new(size, attrs, webrender_api_sender) { Ok(thread) => { result_chan.send(Ok(())).unwrap(); thread @@ -217,8 +89,8 @@ impl WebGLPaintThread { }, CanvasMsg::FromLayout(message) => { match message { - FromLayoutMsg::SendPixelContents(chan) => - painter.send_pixel_contents(chan), + FromLayoutMsg::SendData(chan) => + painter.send_data(chan), } } CanvasMsg::FromPaint(message) => { @@ -239,319 +111,40 @@ impl WebGLPaintThread { }) } - #[inline] - fn context_attributes(&self, sender: IpcSender<GLContextAttributes>) { - sender.send(*self.gl_context.borrow_attributes()).unwrap() - } - - #[inline] - fn send_drawing_buffer_width(&self, sender: IpcSender<i32>) { - sender.send(self.size.width).unwrap() - } - - #[inline] - fn send_drawing_buffer_height(&self, sender: IpcSender<i32>) { - sender.send(self.size.height).unwrap() - } - - fn create_buffer(&self, chan: IpcSender<Option<NonZero<u32>>>) { - let buffer = gl::gen_buffers(1)[0]; - let buffer = if buffer == 0 { - None - } else { - Some(unsafe { NonZero::new(buffer) }) - }; - chan.send(buffer).unwrap(); - } - - fn create_framebuffer(&self, chan: IpcSender<Option<NonZero<u32>>>) { - let framebuffer = gl::gen_framebuffers(1)[0]; - let framebuffer = if framebuffer == 0 { - None - } else { - Some(unsafe { NonZero::new(framebuffer) }) - }; - chan.send(framebuffer).unwrap(); - } - - fn create_renderbuffer(&self, chan: IpcSender<Option<NonZero<u32>>>) { - let renderbuffer = gl::gen_renderbuffers(1)[0]; - let renderbuffer = if renderbuffer == 0 { - None - } else { - Some(unsafe { NonZero::new(renderbuffer) }) - }; - chan.send(renderbuffer).unwrap(); - } - - fn create_texture(&self, chan: IpcSender<Option<NonZero<u32>>>) { - let texture = gl::gen_framebuffers(1)[0]; - let texture = if texture == 0 { - None - } else { - Some(unsafe { NonZero::new(texture) }) - }; - chan.send(texture).unwrap(); - } - - fn create_program(&self, chan: IpcSender<Option<NonZero<u32>>>) { - let program = gl::create_program(); - let program = if program == 0 { - None - } else { - Some(unsafe { NonZero::new(program) }) - }; - - chan.send(program).unwrap(); - } - - fn create_shader(&self, shader_type: u32, chan: IpcSender<Option<NonZero<u32>>>) { - let shader = gl::create_shader(shader_type); - let shader = if shader == 0 { - None - } else { - Some(unsafe { NonZero::new(shader) }) - }; - chan.send(shader).unwrap(); - } - - #[inline] - fn bind_framebuffer(&self, target: u32, request: WebGLFramebufferBindingRequest) { - let id = match request { - WebGLFramebufferBindingRequest::Explicit(id) => id, - WebGLFramebufferBindingRequest::Default => - self.gl_context.borrow_draw_buffer().unwrap().get_framebuffer(), - }; - - gl::bind_framebuffer(target, id); - } - - #[inline] - fn compile_shader(&self, shader_id: u32, source: String) { - gl::shader_source(shader_id, &[source.as_bytes()]); - gl::compile_shader(shader_id); - } - - fn attrib_location(&self, program_id: u32, name: String, chan: IpcSender<Option<i32>> ) { - let attrib_location = gl::get_attrib_location(program_id, &name); - - let attrib_location = if attrib_location == -1 { - None - } else { - Some(attrib_location) - }; - - chan.send(attrib_location).unwrap(); - } - - fn parameter(&self, - param_id: u32, - chan: IpcSender<WebGLResult<WebGLParameter>>) { - let result = match param_id { - gl::ACTIVE_TEXTURE | - //gl::ALPHA_BITS | - gl::BLEND_DST_ALPHA | - gl::BLEND_DST_RGB | - gl::BLEND_EQUATION_ALPHA | - gl::BLEND_EQUATION_RGB | - gl::BLEND_SRC_ALPHA | - gl::BLEND_SRC_RGB | - //gl::BLUE_BITS | - gl::CULL_FACE_MODE | - //gl::DEPTH_BITS | - gl::DEPTH_FUNC | - gl::FRONT_FACE | - //gl::GENERATE_MIPMAP_HINT | - //gl::GREEN_BITS | - //gl::IMPLEMENTATION_COLOR_READ_FORMAT | - //gl::IMPLEMENTATION_COLOR_READ_TYPE | - gl::MAX_COMBINED_TEXTURE_IMAGE_UNITS | - gl::MAX_CUBE_MAP_TEXTURE_SIZE | - //gl::MAX_FRAGMENT_UNIFORM_VECTORS | - gl::MAX_RENDERBUFFER_SIZE | - gl::MAX_TEXTURE_IMAGE_UNITS | - gl::MAX_TEXTURE_SIZE | - //gl::MAX_VARYING_VECTORS | - gl::MAX_VERTEX_ATTRIBS | - gl::MAX_VERTEX_TEXTURE_IMAGE_UNITS | - //gl::MAX_VERTEX_UNIFORM_VECTORS | - gl::PACK_ALIGNMENT | - //gl::RED_BITS | - gl::SAMPLE_BUFFERS | - gl::SAMPLES | - gl::STENCIL_BACK_FAIL | - gl::STENCIL_BACK_FUNC | - gl::STENCIL_BACK_PASS_DEPTH_FAIL | - gl::STENCIL_BACK_PASS_DEPTH_PASS | - gl::STENCIL_BACK_REF | - gl::STENCIL_BACK_VALUE_MASK | - gl::STENCIL_BACK_WRITEMASK | - //gl::STENCIL_BITS | - gl::STENCIL_CLEAR_VALUE | - gl::STENCIL_FAIL | - gl::STENCIL_FUNC | - gl::STENCIL_PASS_DEPTH_FAIL | - gl::STENCIL_PASS_DEPTH_PASS | - gl::STENCIL_REF | - gl::STENCIL_VALUE_MASK | - gl::STENCIL_WRITEMASK | - gl::SUBPIXEL_BITS | - gl::UNPACK_ALIGNMENT => - //gl::UNPACK_COLORSPACE_CONVERSION_WEBGL => - Ok(WebGLParameter::Int(gl::get_integer_v(param_id))), - - gl::BLEND | - gl::CULL_FACE | - gl::DEPTH_TEST | - gl::DEPTH_WRITEMASK | - gl::DITHER | - gl::POLYGON_OFFSET_FILL | - gl::SAMPLE_COVERAGE_INVERT | - gl::STENCIL_TEST => - //gl::UNPACK_FLIP_Y_WEBGL | - //gl::UNPACK_PREMULTIPLY_ALPHA_WEBGL => - Ok(WebGLParameter::Bool(gl::get_boolean_v(param_id) != 0)), - - gl::DEPTH_CLEAR_VALUE | - gl::LINE_WIDTH | - gl::POLYGON_OFFSET_FACTOR | - gl::POLYGON_OFFSET_UNITS | - gl::SAMPLE_COVERAGE_VALUE => - Ok(WebGLParameter::Float(gl::get_float_v(param_id))), - - gl::VERSION => Ok(WebGLParameter::String("WebGL 1.0".to_owned())), - gl::RENDERER | - gl::VENDOR => Ok(WebGLParameter::String("Mozilla/Servo".to_owned())), - gl::SHADING_LANGUAGE_VERSION => Ok(WebGLParameter::String("WebGL GLSL ES 1.0".to_owned())), - - // TODO(zbarsky, ecoal95): Implement support for the following valid parameters - // Float32Array - gl::ALIASED_LINE_WIDTH_RANGE | - //gl::ALIASED_POINT_SIZE_RANGE | - //gl::BLEND_COLOR | - gl::COLOR_CLEAR_VALUE | - gl::DEPTH_RANGE | - - // WebGLBuffer - gl::ARRAY_BUFFER_BINDING | - gl::ELEMENT_ARRAY_BUFFER_BINDING | - - // WebGLFrameBuffer - gl::FRAMEBUFFER_BINDING | - - // WebGLRenderBuffer - gl::RENDERBUFFER_BINDING | - - // WebGLProgram - gl::CURRENT_PROGRAM | - - // WebGLTexture - gl::TEXTURE_BINDING_2D | - gl::TEXTURE_BINDING_CUBE_MAP | - - // sequence<GlBoolean> - gl::COLOR_WRITEMASK | - - // Uint32Array - gl::COMPRESSED_TEXTURE_FORMATS | - - // Int32Array - gl::MAX_VIEWPORT_DIMS | - gl::SCISSOR_BOX | - gl::VIEWPORT => Err(WebGLError::InvalidEnum), - - // Invalid parameters - _ => Err(WebGLError::InvalidEnum) - }; - - chan.send(result).unwrap(); - } - - fn buffer_parameter(&self, - target: u32, - param_id: u32, - chan: IpcSender<WebGLResult<WebGLParameter>>) { - let result = match param_id { - gl::BUFFER_SIZE | - gl::BUFFER_USAGE => - Ok(WebGLParameter::Int(gl::get_buffer_parameter_iv(target, param_id))), - _ => Err(WebGLError::InvalidEnum), - }; - - chan.send(result).unwrap(); - } - - fn program_parameter(&self, - program_id: u32, - param_id: u32, - chan: IpcSender<WebGLResult<WebGLParameter>>) { - let result = match param_id { - gl::DELETE_STATUS | - gl::LINK_STATUS | - gl::VALIDATE_STATUS => - Ok(WebGLParameter::Bool(gl::get_program_iv(program_id, param_id) != 0)), - gl::ATTACHED_SHADERS | - gl::ACTIVE_ATTRIBUTES | - gl::ACTIVE_UNIFORMS => - Ok(WebGLParameter::Int(gl::get_program_iv(program_id, param_id))), - _ => Err(WebGLError::InvalidEnum), - }; - - chan.send(result).unwrap(); - } - - fn shader_parameter(&self, - shader_id: u32, - param_id: u32, - chan: IpcSender<WebGLResult<WebGLParameter>>) { - let result = match param_id { - gl::SHADER_TYPE => - Ok(WebGLParameter::Int(gl::get_shader_iv(shader_id, param_id))), - gl::DELETE_STATUS | - gl::COMPILE_STATUS => - Ok(WebGLParameter::Bool(gl::get_shader_iv(shader_id, param_id) != 0)), - _ => Err(WebGLError::InvalidEnum), - }; - - chan.send(result).unwrap(); - } - - fn uniform_location(&self, program_id: u32, name: String, chan: IpcSender<Option<i32>>) { - let location = gl::get_uniform_location(program_id, &name); - let location = if location == -1 { - None - } else { - Some(location) - }; + fn send_data(&mut self, chan: IpcSender<CanvasData>) { + match self.data { + WebGLPaintTaskData::Servo(_) => { + let width = self.size.width as usize; + let height = self.size.height as usize; + + let mut pixels = gl::read_pixels(0, 0, + self.size.width as gl::GLsizei, + self.size.height as gl::GLsizei, + gl::RGBA, gl::UNSIGNED_BYTE); + // flip image vertically (texture is upside down) + let orig_pixels = pixels.clone(); + let stride = width * 4; + for y in 0..height { + let dst_start = y * stride; + let src_start = (height - y - 1) * stride; + let src_slice = &orig_pixels[src_start .. src_start + stride]; + (&mut pixels[dst_start .. dst_start + stride]).clone_from_slice(&src_slice[..stride]); + } - chan.send(location).unwrap(); - } + // rgba -> bgra + byte_swap(&mut pixels); - fn send_pixel_contents(&mut self, chan: IpcSender<IpcSharedMemory>) { - // FIXME(#5652, dmarcos) Instead of a readback strategy we have - // to layerize the canvas. - // TODO(pcwalton): We'd save a copy if we had an `IpcSharedMemoryBuilder` abstraction that - // allowed you to mutate in-place before freezing the object for sending. - let width = self.size.width as usize; - let height = self.size.height as usize; + let pixel_data = CanvasPixelData { + image_data: IpcSharedMemory::from_bytes(&pixels[..]), + image_key: None, + }; - let mut pixels = gl::read_pixels(0, 0, - self.size.width as gl::GLsizei, - self.size.height as gl::GLsizei, - gl::RGBA, gl::UNSIGNED_BYTE); - // flip image vertically (texture is upside down) - let orig_pixels = pixels.clone(); - let stride = width * 4; - for y in 0..height { - let dst_start = y * stride; - let src_start = (height - y - 1) * stride; - let src_slice = &orig_pixels[src_start .. src_start + stride]; - (&mut pixels[dst_start .. dst_start + stride]).clone_from_slice(&src_slice[..stride]); + chan.send(CanvasData::Pixels(pixel_data)).unwrap(); + } + WebGLPaintTaskData::WebRender(_, id) => { + chan.send(CanvasData::WebGL(id)).unwrap(); + } } - - // rgba -> bgra - byte_swap(&mut pixels); - chan.send(IpcSharedMemory::from_bytes(&pixels[..])).unwrap(); } fn send_native_surface(&self, _: Sender<NativeSurface>) { @@ -561,18 +154,28 @@ impl WebGLPaintThread { } fn recreate(&mut self, size: Size2D<i32>) -> Result<(), &'static str> { - if size.width > self.original_context_size.width || - size.height > self.original_context_size.height { - try!(self.gl_context.resize(size)); - self.size = self.gl_context.borrow_draw_buffer().unwrap().size(); - } else { - self.size = size; - unsafe { gl::Scissor(0, 0, size.width, size.height); } + match self.data { + WebGLPaintTaskData::Servo(ref mut context) => { + if size.width > self.size.width || + size.height > self.size.height { + try!(context.resize(size)); + self.size = context.borrow_draw_buffer().unwrap().size(); + } else { + self.size = size; + unsafe { gl::Scissor(0, 0, size.width, size.height); } + } + } + WebGLPaintTaskData::WebRender(_, _) => { + // TODO + } } + Ok(()) } fn init(&mut self) { - self.gl_context.make_current().unwrap(); + if let WebGLPaintTaskData::Servo(ref context) = self.data { + context.make_current().unwrap(); + } } } diff --git a/components/canvas_traits/Cargo.toml b/components/canvas_traits/Cargo.toml index 0b5962e5802..d1985af2a6b 100644 --- a/components/canvas_traits/Cargo.toml +++ b/components/canvas_traits/Cargo.toml @@ -34,6 +34,9 @@ path = "../plugins" [dependencies.util] path = "../util" +[dependencies.webrender_traits] +git = "https://github.com/glennw/webrender_traits" + [dependencies] cssparser = {version = "0.5.3", features = ["heap_size", "serde-serialization"]} euclid = {version = "0.6.2", features = ["plugins"]} diff --git a/components/canvas_traits/lib.rs b/components/canvas_traits/lib.rs index 4dc0a5904f8..8d6769da02a 100644 --- a/components/canvas_traits/lib.rs +++ b/components/canvas_traits/lib.rs @@ -5,7 +5,6 @@ #![crate_name = "canvas_traits"] #![crate_type = "rlib"] #![feature(custom_derive)] -#![feature(nonzero)] #![feature(plugin)] #![plugin(heapsize_plugin, plugins, serde_macros)] @@ -20,13 +19,13 @@ extern crate layers; extern crate offscreen_gl_context; extern crate serde; extern crate util; +extern crate webrender_traits; use azure::azure::{AzColor, AzFloat}; use azure::azure_hl::{CapStyle, CompositionOp, JoinStyle}; use azure::azure_hl::{ColorPattern, DrawTarget, Pattern}; use azure::azure_hl::{ExtendMode, GradientStop, LinearGradientPattern, RadialGradientPattern}; use azure::azure_hl::{SurfaceFormat, SurfacePattern}; -use core::nonzero::NonZero; use cssparser::RGBA; use euclid::matrix2d::Matrix2D; use euclid::point::Point2D; @@ -35,13 +34,14 @@ use euclid::size::Size2D; use gfx_traits::color; use ipc_channel::ipc::{IpcSender, IpcSharedMemory}; use layers::platform::surface::NativeSurface; -use offscreen_gl_context::GLContextAttributes; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::default::Default; -use std::fmt; use std::str::FromStr; use std::sync::mpsc::Sender; +pub use webrender_traits::{WebGLFramebufferBindingRequest, WebGLError, WebGLParameter, WebGLResult, WebGLContextId}; +pub use webrender_traits::WebGLCommand as CanvasWebGLMsg; + #[derive(Clone, Deserialize, Serialize)] pub enum FillRule { Nonzero, @@ -64,8 +64,20 @@ pub enum CanvasCommonMsg { } #[derive(Clone, Deserialize, Serialize)] +pub enum CanvasData { + Pixels(CanvasPixelData), + WebGL(WebGLContextId), +} + +#[derive(Clone, Deserialize, Serialize)] +pub struct CanvasPixelData { + pub image_data: IpcSharedMemory, + pub image_key: Option<webrender_traits::ImageKey>, +} + +#[derive(Clone, Deserialize, Serialize)] pub enum FromLayoutMsg { - SendPixelContents(IpcSender<IpcSharedMemory>), + SendData(IpcSender<CanvasData>), } #[derive(Clone)] @@ -124,178 +136,6 @@ pub enum Canvas2dMsg { SetShadowColor(RGBA), } -#[derive(Clone, Deserialize, Serialize)] -pub enum CanvasWebGLMsg { - GetContextAttributes(IpcSender<GLContextAttributes>), - ActiveTexture(u32), - BlendColor(f32, f32, f32, f32), - BlendEquation(u32), - BlendEquationSeparate(u32, u32), - BlendFunc(u32, u32), - BlendFuncSeparate(u32, u32, u32, u32), - AttachShader(u32, u32), - BindAttribLocation(u32, u32, String), - BufferData(u32, Vec<u8>, u32), - BufferSubData(u32, isize, Vec<u8>), - Clear(u32), - ClearColor(f32, f32, f32, f32), - ClearDepth(f64), - ClearStencil(i32), - ColorMask(bool, bool, bool, bool), - CullFace(u32), - FrontFace(u32), - DepthFunc(u32), - DepthMask(bool), - DepthRange(f64, f64), - Enable(u32), - Disable(u32), - CompileShader(u32, String), - CreateBuffer(IpcSender<Option<NonZero<u32>>>), - CreateFramebuffer(IpcSender<Option<NonZero<u32>>>), - CreateRenderbuffer(IpcSender<Option<NonZero<u32>>>), - CreateTexture(IpcSender<Option<NonZero<u32>>>), - CreateProgram(IpcSender<Option<NonZero<u32>>>), - CreateShader(u32, IpcSender<Option<NonZero<u32>>>), - DeleteBuffer(u32), - DeleteFramebuffer(u32), - DeleteRenderbuffer(u32), - DeleteTexture(u32), - DeleteProgram(u32), - DeleteShader(u32), - BindBuffer(u32, u32), - BindFramebuffer(u32, WebGLFramebufferBindingRequest), - BindRenderbuffer(u32, u32), - BindTexture(u32, u32), - DrawArrays(u32, i32, i32), - DrawElements(u32, i32, u32, i64), - EnableVertexAttribArray(u32), - GetBufferParameter(u32, u32, IpcSender<WebGLResult<WebGLParameter>>), - GetParameter(u32, IpcSender<WebGLResult<WebGLParameter>>), - GetProgramParameter(u32, u32, IpcSender<WebGLResult<WebGLParameter>>), - GetShaderParameter(u32, u32, IpcSender<WebGLResult<WebGLParameter>>), - GetAttribLocation(u32, String, IpcSender<Option<i32>>), - GetUniformLocation(u32, String, IpcSender<Option<i32>>), - PolygonOffset(f32, f32), - Scissor(i32, i32, i32, i32), - Hint(u32, u32), - LineWidth(f32), - PixelStorei(u32, i32), - LinkProgram(u32), - Uniform1f(i32, f32), - Uniform4f(i32, f32, f32, f32, f32), - UseProgram(u32), - VertexAttrib(u32, f32, f32, f32, f32), - VertexAttribPointer2f(u32, i32, bool, i32, u32), - Viewport(i32, i32, i32, i32), - TexImage2D(u32, i32, i32, i32, i32, u32, u32, Vec<u8>), - TexParameteri(u32, u32, i32), - TexParameterf(u32, u32, f32), - DrawingBufferWidth(IpcSender<i32>), - DrawingBufferHeight(IpcSender<i32>), -} - -impl fmt::Debug for CanvasWebGLMsg { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use CanvasWebGLMsg::*; - let name = match *self { - GetContextAttributes(..) => "GetContextAttributes", - ActiveTexture(..) => "ActiveTexture", - BlendColor(..) => "BlendColor", - BlendEquation(..) => "BlendEquation", - BlendEquationSeparate(..) => "BlendEquationSeparate", - BlendFunc(..) => "BlendFunc", - BlendFuncSeparate(..) => "BlendFuncSeparate", - AttachShader(..) => "AttachShader", - BindAttribLocation(..) => "BindAttribLocation", - BufferData(..) => "BufferData", - BufferSubData(..) => "BufferSubData", - Clear(..) => "Clear", - ClearColor(..) => "ClearColor", - ClearDepth(..) => "ClearDepth", - ClearStencil(..) => "ClearStencil", - ColorMask(..) => "ColorMask", - CullFace(..) => "CullFace", - FrontFace(..) => "FrontFace", - DepthFunc(..) => "DepthFunc", - DepthMask(..) => "DepthMask", - DepthRange(..) => "DepthRange", - Enable(..) => "Enable", - Disable(..) => "Disable", - CompileShader(..) => "CompileShader", - CreateBuffer(..) => "CreateBuffer", - CreateFramebuffer(..) => "CreateFramebuffer", - CreateRenderbuffer(..) => "CreateRenderbuffer", - CreateTexture(..) => "CreateTexture", - CreateProgram(..) => "CreateProgram", - CreateShader(..) => "CreateShader", - DeleteBuffer(..) => "DeleteBuffer", - DeleteFramebuffer(..) => "DeleteFramebuffer", - DeleteRenderbuffer(..) => "DeleteRenderBuffer", - DeleteTexture(..) => "DeleteTexture", - DeleteProgram(..) => "DeleteProgram", - DeleteShader(..) => "DeleteShader", - BindBuffer(..) => "BindBuffer", - BindFramebuffer(..) => "BindFramebuffer", - BindRenderbuffer(..) => "BindRenderbuffer", - BindTexture(..) => "BindTexture", - DrawArrays(..) => "DrawArrays", - DrawElements(..) => "DrawElements", - EnableVertexAttribArray(..) => "EnableVertexAttribArray", - GetBufferParameter(..) => "GetBufferParameter", - GetParameter(..) => "GetParameter", - GetProgramParameter(..) => "GetProgramParameter", - GetShaderParameter(..) => "GetShaderParameter", - GetAttribLocation(..) => "GetAttribLocation", - GetUniformLocation(..) => "GetUniformLocation", - PolygonOffset(..) => "PolygonOffset", - Scissor(..) => "Scissor", - Hint(..) => "Hint", - LineWidth(..) => "LineWidth", - PixelStorei(..) => "PixelStorei", - LinkProgram(..) => "LinkProgram", - Uniform4f(..) => "Uniform4f", - Uniform1f(..) => "Uniform1f", - UseProgram(..) => "UseProgram", - VertexAttrib(..) => "VertexAttrib", - VertexAttribPointer2f(..) => "VertexAttribPointer2f", - Viewport(..) => "Viewport", - TexImage2D(..) => "TexImage2D", - TexParameteri(..) => "TexParameteri", - TexParameterf(..) => "TexParameterf", - DrawingBufferWidth(..) => "DrawingBufferWidth", - DrawingBufferHeight(..) => "DrawingBufferHeight", - }; - - write!(f, "CanvasWebGLMsg::{}(..)", name) - } -} - -#[derive(Clone, Copy, PartialEq, Deserialize, Serialize, HeapSizeOf)] -pub enum WebGLError { - InvalidEnum, - InvalidOperation, - InvalidValue, - OutOfMemory, - ContextLost, -} - -pub type WebGLResult<T> = Result<T, WebGLError>; - -#[derive(Clone, Deserialize, Serialize)] -pub enum WebGLFramebufferBindingRequest { - Explicit(u32), - Default, -} - -#[derive(Clone, Deserialize, Serialize)] -pub enum WebGLParameter { - Int(i32), - Bool(bool), - String(String), - Float(f32), - Invalid, -} - #[derive(Clone, Deserialize, Serialize, HeapSizeOf)] pub struct CanvasGradientStop { pub offset: f64, diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml index a190ddb73f2..389b669d607 100644 --- a/components/compositing/Cargo.toml +++ b/components/compositing/Cargo.toml @@ -81,6 +81,12 @@ git = "https://github.com/servo/gaol" [target.aarch64-unknown-linux-gnu.dependencies.gaol] git = "https://github.com/servo/gaol" +[dependencies.webrender_traits] +git = "https://github.com/glennw/webrender_traits" + +[dependencies.webrender] +git = "https://github.com/glennw/webrender" + [dependencies] app_units = {version = "0.2.1", features = ["plugins"]} euclid = {version = "0.6.2", features = ["plugins"]} diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index f75126cab28..e8ac9a88c22 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -6,7 +6,7 @@ use CompositorMsg as ConstellationMsg; use app_units::Au; use compositor_layer::{CompositorData, CompositorLayer, RcCompositorLayer, WantsScrollEventsFlag}; use compositor_thread::{CompositorEventListener, CompositorProxy}; -use compositor_thread::{CompositorReceiver, InitialCompositorState, Msg}; +use compositor_thread::{CompositorReceiver, InitialCompositorState, Msg, RenderListener}; use constellation::SendableFrameTree; use euclid::point::TypedPoint2D; use euclid::rect::TypedRect; @@ -27,15 +27,15 @@ use layers::rendergl; use layers::rendergl::RenderContext; use layers::scene::Scene; use layout_traits::LayoutControlChan; -use msg::constellation_msg::{Image, PixelFormat}; +use msg::constellation_msg::{ConvertPipelineIdFromWebRender, ConvertPipelineIdToWebRender, Image, PixelFormat}; use msg::constellation_msg::{Key, KeyModifiers, KeyState, LoadData}; use msg::constellation_msg::{NavigationDirection, PipelineId, WindowSizeData}; use pipeline::CompositionPipeline; use profile_traits::mem::{self, ReportKind, Reporter, ReporterRequest}; use profile_traits::time::{self, ProfilerCategory, profile}; -use script_traits::CompositorEvent::{MouseMoveEvent, TouchEvent}; +use script_traits::CompositorEvent::{MouseMoveEvent, MouseButtonEvent, TouchEvent}; use script_traits::{AnimationState, ConstellationControlMsg, LayoutControlMsg}; -use script_traits::{MouseButton, TouchEventType, TouchId}; +use script_traits::{MouseButton, MouseEventType, TouchEventType, TouchId}; use scrolling::ScrollingTimerProxy; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::{HashMap, HashSet}; @@ -51,6 +51,8 @@ use url::Url; use util::geometry::{PagePx, ScreenPx, ViewportPx}; use util::opts; use util::print_tree::PrintTree; +use webrender; +use webrender_traits; use windowing::{self, MouseWindowEvent, WindowEvent, WindowMethods, WindowNavigateMsg}; #[derive(Debug, PartialEq)] @@ -192,6 +194,12 @@ pub struct IOCompositor<Window: WindowMethods> { /// The id of the pipeline that was last sent a mouse move event, if any. last_mouse_move_recipient: Option<PipelineId>, + + /// The webrender renderer, if enabled. + webrender: Option<webrender::Renderer>, + + /// The webrender interface, if enabled. + webrender_api: Option<webrender_traits::RenderApi>, } pub struct ScrollZoomEvent { @@ -261,7 +269,23 @@ pub enum CompositeTarget { PngFile } -fn initialize_png(width: usize, height: usize) -> (Vec<gl::GLuint>, Vec<gl::GLuint>) { +struct RenderTargetInfo { + framebuffer_ids: Vec<gl::GLuint>, + texture_ids: Vec<gl::GLuint>, + renderbuffer_ids: Vec<gl::GLuint>, +} + +impl RenderTargetInfo { + fn empty() -> RenderTargetInfo { + RenderTargetInfo { + framebuffer_ids: Vec::new(), + texture_ids: Vec::new(), + renderbuffer_ids: Vec::new() + } + } +} + +fn initialize_png(width: usize, height: usize) -> RenderTargetInfo { let framebuffer_ids = gl::gen_framebuffers(1); gl::bind_framebuffer(gl::FRAMEBUFFER, framebuffer_ids[0]); @@ -278,13 +302,64 @@ fn initialize_png(width: usize, height: usize) -> (Vec<gl::GLuint>, Vec<gl::GLui gl::bind_texture(gl::TEXTURE_2D, 0); - (framebuffer_ids, texture_ids) + let renderbuffer_ids = if opts::get().use_webrender { + let renderbuffer_ids = gl::gen_renderbuffers(1); + gl::bind_renderbuffer(gl::RENDERBUFFER, renderbuffer_ids[0]); + gl::renderbuffer_storage(gl::RENDERBUFFER, + gl::STENCIL_INDEX8, + width as GLsizei, + height as GLsizei); + gl::framebuffer_renderbuffer(gl::FRAMEBUFFER, + gl::STENCIL_ATTACHMENT, + gl::RENDERBUFFER, + renderbuffer_ids[0]); + renderbuffer_ids + } else { + Vec::new() + }; + + RenderTargetInfo { + framebuffer_ids: framebuffer_ids, + texture_ids: texture_ids, + renderbuffer_ids: renderbuffer_ids + } } pub fn reporter_name() -> String { "compositor-reporter".to_owned() } +struct RenderNotifier { + compositor_proxy: Box<CompositorProxy>, + constellation_chan: Sender<ConstellationMsg>, +} + +impl RenderNotifier { + fn new(compositor_proxy: Box<CompositorProxy>, + constellation_chan: Sender<ConstellationMsg>) -> RenderNotifier { + RenderNotifier { + compositor_proxy: compositor_proxy, + constellation_chan: constellation_chan, + } + } +} + +impl webrender_traits::RenderNotifier for RenderNotifier { + fn new_frame_ready(&mut self) { + self.compositor_proxy.recomposite(); + } + + fn pipeline_size_changed(&mut self, + pipeline_id: webrender_traits::PipelineId, + size: Option<Size2D<f32>>) { + let pipeline_id = pipeline_id.from_webrender(); + let size = size.unwrap_or(Size2D::zero()); + + self.constellation_chan.send(ConstellationMsg::FrameSize(pipeline_id, + size)).unwrap(); + } +} + impl<Window: WindowMethods> IOCompositor<Window> { fn new(window: Rc<Window>, state: InitialCompositorState) -> IOCompositor<Window> { @@ -306,6 +381,11 @@ impl<Window: WindowMethods> IOCompositor<Window> { Some(_) => CompositeTarget::PngFile, None => CompositeTarget::Window }; + + let webrender_api = state.webrender_api_sender.map(|sender| { + sender.create_api() + }); + let native_display = window.native_display(); IOCompositor { window: window, @@ -345,6 +425,8 @@ impl<Window: WindowMethods> IOCompositor<Window> { surface_map: SurfaceMap::new(BUFFER_MAP_SIZE), pending_subpages: HashSet::new(), last_mouse_move_recipient: None, + webrender: state.webrender, + webrender_api: webrender_api, } } @@ -352,6 +434,14 @@ impl<Window: WindowMethods> IOCompositor<Window> { -> IOCompositor<Window> { let mut compositor = IOCompositor::new(window, state); + if let Some(ref mut webrender) = compositor.webrender { + let compositor_proxy_for_webrender = compositor.channel_to_self + .clone_compositor_proxy(); + let render_notifier = RenderNotifier::new(compositor_proxy_for_webrender, + compositor.constellation_chan.clone()); + webrender.set_render_notifier(Box::new(render_notifier)); + } + // Set the size of the root layer. compositor.update_zoom_transform(); @@ -674,6 +764,11 @@ impl<Window: WindowMethods> IOCompositor<Window> { self.root_pipeline = Some(frame_tree.pipeline.clone()); + if let Some(ref webrender_api) = self.webrender_api { + let pipeline_id = frame_tree.pipeline.id.to_webrender(); + webrender_api.set_root_pipeline(pipeline_id); + } + // If we have an old root layer, release all old tiles before replacing it. let old_root_layer = self.scene.root.take(); if let Some(ref old_root_layer) = old_root_layer { @@ -1172,6 +1267,36 @@ impl<Window: WindowMethods> IOCompositor<Window> { MouseWindowEvent::MouseDown(_, p) => p, MouseWindowEvent::MouseUp(_, p) => p, }; + + if let Some(ref webrender_api) = self.webrender_api { + let root_pipeline_id = match self.get_root_pipeline_id() { + Some(root_pipeline_id) => root_pipeline_id, + None => return, + }; + let root_pipeline = match self.pipeline(root_pipeline_id) { + Some(root_pipeline) => root_pipeline, + None => return, + }; + + let translated_point = + webrender_api.translate_point_to_layer_space(&point.to_untyped()); + let event_to_send = match mouse_window_event { + MouseWindowEvent::Click(button, _) => { + MouseButtonEvent(MouseEventType::Click, button, translated_point) + } + MouseWindowEvent::MouseDown(button, _) => { + MouseButtonEvent(MouseEventType::MouseDown, button, translated_point) + } + MouseWindowEvent::MouseUp(button, _) => { + MouseButtonEvent(MouseEventType::MouseUp, button, translated_point) + } + }; + root_pipeline.script_chan + .send(ConstellationControlMsg::SendEvent(root_pipeline_id, + event_to_send)) + .unwrap(); + } + match self.find_topmost_layer_at_point(point / self.scene.scale) { Some(result) => result.layer.send_mouse_event(self, mouse_window_event, result.point), None => {}, @@ -1184,6 +1309,25 @@ impl<Window: WindowMethods> IOCompositor<Window> { return } + if let Some(ref webrender_api) = self.webrender_api { + let root_pipeline_id = match self.get_root_pipeline_id() { + Some(root_pipeline_id) => root_pipeline_id, + None => return, + }; + let root_pipeline = match self.pipeline(root_pipeline_id) { + Some(root_pipeline) => root_pipeline, + None => return, + }; + + let translated_point = + webrender_api.translate_point_to_layer_space(&cursor.to_untyped()); + let event_to_send = MouseMoveEvent(Some(translated_point)); + root_pipeline.script_chan + .send(ConstellationControlMsg::SendEvent(root_pipeline_id, + event_to_send)) + .unwrap(); + } + match self.find_topmost_layer_at_point(cursor / self.scene.scale) { Some(result) => { // In the case that the mouse was previously over a different layer, @@ -1285,30 +1429,52 @@ impl<Window: WindowMethods> IOCompositor<Window> { fn process_pending_scroll_events(&mut self) { let had_events = self.pending_scroll_zoom_events.len() > 0; - for event in std_mem::replace(&mut self.pending_scroll_zoom_events, - Vec::new()) { - let delta = event.delta / self.scene.scale; - let cursor = event.cursor.as_f32() / self.scene.scale; - if let Some(ref mut layer) = self.scene.root { - layer.handle_scroll_event(delta, cursor); + match self.webrender_api { + Some(ref webrender_api) => { + // Batch up all scroll events into one, or else we'll do way too much painting. + let mut total_delta = None; + let mut last_cursor = Point2D::zero(); + for scroll_event in self.pending_scroll_zoom_events.drain(..) { + let this_delta = scroll_event.delta / self.scene.scale; + last_cursor = scroll_event.cursor.as_f32() / self.scene.scale; + match total_delta { + None => total_delta = Some(this_delta), + Some(ref mut total_delta) => *total_delta = *total_delta + this_delta, + } + } + // TODO(gw): Support zoom (WR issue #28). + if let Some(total_delta) = total_delta { + webrender_api.scroll(total_delta.to_untyped(), last_cursor.to_untyped()); + } } + None => { + for event in std_mem::replace(&mut self.pending_scroll_zoom_events, + Vec::new()) { + let delta = event.delta / self.scene.scale; + let cursor = event.cursor.as_f32() / self.scene.scale; - if event.magnification != 1.0 { - self.zoom_action = true; - self.zoom_time = precise_time_s(); - self.viewport_zoom = ScaleFactor::new( - (self.viewport_zoom.get() * event.magnification) - .min(self.max_viewport_zoom.as_ref().map_or(MAX_ZOOM, ScaleFactor::get)) - .max(self.min_viewport_zoom.as_ref().map_or(MIN_ZOOM, ScaleFactor::get))); - self.update_zoom_transform(); - } + if let Some(ref mut layer) = self.scene.root { + layer.handle_scroll_event(delta, cursor); + } - self.perform_updates_after_scroll(); - } + if event.magnification != 1.0 { + self.zoom_action = true; + self.zoom_time = precise_time_s(); + self.viewport_zoom = ScaleFactor::new( + (self.viewport_zoom.get() * event.magnification) + .min(self.max_viewport_zoom.as_ref().map_or(MAX_ZOOM, ScaleFactor::get)) + .max(self.min_viewport_zoom.as_ref().map_or(MIN_ZOOM, ScaleFactor::get))); + self.update_zoom_transform(); + } + + self.perform_updates_after_scroll(); + } - if had_events { - self.send_viewport_rects_for_all_layers(); + if had_events { + self.send_viewport_rects_for_all_layers(); + } + } } } @@ -1542,6 +1708,10 @@ impl<Window: WindowMethods> IOCompositor<Window> { /// Returns true if any buffer requests were sent or false otherwise. fn send_buffer_requests_for_all_layers(&mut self) -> bool { + if self.webrender.is_some() { + return false; + } + if let Some(ref root_layer) = self.scene.root { root_layer.update_transform_state(&Matrix4::identity(), &Matrix4::identity(), @@ -1656,7 +1826,15 @@ impl<Window: WindowMethods> IOCompositor<Window> { // frame tree. let mut pipeline_epochs = HashMap::new(); for (id, details) in &self.pipeline_details { - pipeline_epochs.insert(*id, details.current_epoch); + if let Some(ref webrender) = self.webrender { + let webrender_pipeline_id = id.to_webrender(); + if let Some(webrender_traits::Epoch(epoch)) = webrender.current_epoch(webrender_pipeline_id) { + let epoch = Epoch(epoch); + pipeline_epochs.insert(*id, epoch); + } + } else { + pipeline_epochs.insert(*id, details.current_epoch); + } } // Pass the pipeline/epoch states to the constellation and check @@ -1707,7 +1885,7 @@ impl<Window: WindowMethods> IOCompositor<Window> { /// is WindowAndPng Ok(Some(png::Image)) is returned. pub fn composite_specific_target(&mut self, target: CompositeTarget) -> Result<Option<Image>, UnableToComposite> { - if !self.context.is_some() { + if self.context.is_none() && self.webrender.is_none() { return Err(UnableToComposite::NoContext) } let (width, height) = @@ -1716,6 +1894,11 @@ impl<Window: WindowMethods> IOCompositor<Window> { return Err(UnableToComposite::WindowUnprepared) } + if let Some(ref mut webrender) = self.webrender { + assert!(self.context.is_none()); + webrender.update(); + } + let wait_for_stable_image = match target { CompositeTarget::WindowAndPng | CompositeTarget::PngFile => true, CompositeTarget::Window => opts::get().exit_after_load, @@ -1738,8 +1921,8 @@ impl<Window: WindowMethods> IOCompositor<Window> { } } - let (framebuffer_ids, texture_ids) = match target { - CompositeTarget::Window => (vec!(), vec!()), + let render_target_info = match target { + CompositeTarget::Window => RenderTargetInfo::empty(), _ => initialize_png(width, height) }; @@ -1760,7 +1943,10 @@ impl<Window: WindowMethods> IOCompositor<Window> { }; // Paint the scene. - if let Some(ref layer) = self.scene.root { + if let Some(ref mut webrender) = self.webrender { + assert!(self.context.is_none()); + webrender.render(self.window_size.to_untyped()); + } else if let Some(ref layer) = self.scene.root { match self.context { Some(context) => { if let Some((point, size)) = self.viewport { @@ -1790,16 +1976,21 @@ impl<Window: WindowMethods> IOCompositor<Window> { let rv = match target { CompositeTarget::Window => None, CompositeTarget::WindowAndPng => { - let img = self.draw_img(framebuffer_ids, texture_ids, width, height); + let img = self.draw_img(render_target_info, + width, + height); Some(Image { width: img.width(), height: img.height(), format: PixelFormat::RGB8, bytes: IpcSharedMemory::from_bytes(&*img), + id: None, }) } CompositeTarget::PngFile => { - let img = self.draw_img(framebuffer_ids, texture_ids, width, height); + let img = self.draw_img(render_target_info, + width, + height); let path = opts::get().output_file.as_ref().unwrap(); let mut file = File::create(path).unwrap(); DynamicImage::ImageRgb8(img).save(&mut file, ImageFormat::PNG).unwrap(); @@ -1820,8 +2011,7 @@ impl<Window: WindowMethods> IOCompositor<Window> { } fn draw_img(&self, - framebuffer_ids: Vec<gl::GLuint>, - texture_ids: Vec<gl::GLuint>, + render_target_info: RenderTargetInfo, width: usize, height: usize) -> RgbImage { @@ -1832,8 +2022,11 @@ impl<Window: WindowMethods> IOCompositor<Window> { gl::bind_framebuffer(gl::FRAMEBUFFER, 0); - gl::delete_buffers(&texture_ids); - gl::delete_frame_buffers(&framebuffer_ids); + gl::delete_buffers(&render_target_info.texture_ids); + gl::delete_frame_buffers(&render_target_info.framebuffer_ids); + if opts::get().use_webrender { + gl::delete_renderbuffers(&render_target_info.renderbuffer_ids); + } // flip image vertically (texture is upside down) let orig_pixels = pixels.clone(); @@ -1859,10 +2052,12 @@ impl<Window: WindowMethods> IOCompositor<Window> { } fn initialize_compositing(&mut self) { - let show_debug_borders = opts::get().show_debug_borders; - self.context = Some(rendergl::RenderContext::new(self.native_display.clone(), - show_debug_borders, - opts::get().output_file.is_some())) + if self.webrender.is_none() { + let show_debug_borders = opts::get().show_debug_borders; + self.context = Some(rendergl::RenderContext::new(self.native_display.clone(), + show_debug_borders, + opts::get().output_file.is_some())) + } } fn find_topmost_layer_at_point_for_layer(&self, @@ -1951,6 +2146,10 @@ impl<Window: WindowMethods> IOCompositor<Window> { self.surface_map.insert_surfaces(&self.native_display, surfaces); } + fn get_root_pipeline_id(&self) -> Option<PipelineId> { + self.scene.root.as_ref().map(|root_layer| root_layer.extra_data.borrow().pipeline_id) + } + #[allow(dead_code)] fn dump_layer_tree(&self) { if !opts::get().dump_layer_tree { @@ -2076,19 +2275,37 @@ impl<Window> CompositorEventListener for IOCompositor<Window> where Window: Wind /// /// This is used when resizing the window. fn repaint_synchronously(&mut self) { - while self.shutdown_state != ShutdownState::ShuttingDown { - let msg = self.port.recv_compositor_msg(); - let received_new_buffers = match msg { - Msg::AssignPaintedBuffers(..) => true, - _ => false, - }; - let keep_going = self.handle_browser_message(msg); - if received_new_buffers { - self.composite(); - break + if self.webrender.is_none() { + while self.shutdown_state != ShutdownState::ShuttingDown { + let msg = self.port.recv_compositor_msg(); + let received_new_buffers = match msg { + Msg::AssignPaintedBuffers(..) => true, + _ => false, + }; + let keep_going = self.handle_browser_message(msg); + if received_new_buffers { + self.composite(); + break + } + if !keep_going { + break + } } - if !keep_going { - break + } else { + while self.shutdown_state != ShutdownState::ShuttingDown { + let msg = self.port.recv_compositor_msg(); + let need_recomposite = match msg { + Msg::RecompositeAfterScroll => true, + _ => false, + }; + let keep_going = self.handle_browser_message(msg); + if need_recomposite { + self.composite(); + break + } + if !keep_going { + break + } } } } diff --git a/components/compositing/compositor_thread.rs b/components/compositing/compositor_thread.rs index 618bac9674b..f9f0c03d1e3 100644 --- a/components/compositing/compositor_thread.rs +++ b/components/compositing/compositor_thread.rs @@ -26,6 +26,8 @@ use url::Url; use windowing::{WindowEvent, WindowMethods}; pub use constellation::SendableFrameTree; pub use windowing; +use webrender; +use webrender_traits; /// Sends messages to the compositor. This is a trait supplied by the port because the method used /// to communicate with the compositor may have to kick OS event loops awake, communicate cross- @@ -100,6 +102,16 @@ pub fn run_script_listener_thread(compositor_proxy: Box<CompositorProxy + 'stati } } +pub trait RenderListener { + fn recomposite(&mut self); +} + +impl RenderListener for Box<CompositorProxy + 'static> { + fn recomposite(&mut self) { + self.send(Msg::RecompositeAfterScroll); + } +} + /// Implementation of the abstract `PaintListener` interface. impl PaintListener for Box<CompositorProxy + 'static + Send> { fn native_display(&mut self) -> Option<NativeDisplay> { @@ -301,4 +313,7 @@ pub struct InitialCompositorState { pub time_profiler_chan: time::ProfilerChan, /// A channel to the memory profiler thread. pub mem_profiler_chan: mem::ProfilerChan, + /// Instance of webrender API if enabled + pub webrender: Option<webrender::Renderer>, + pub webrender_api_sender: Option<webrender_traits::RenderApiSender>, } diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index a08687bb680..b7a57f7069e 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -63,6 +63,7 @@ use url::Url; use util::geometry::PagePx; use util::thread::spawn_named; use util::{opts, prefs}; +use webrender_traits; #[derive(Debug, PartialEq)] enum ReadyToSave { @@ -181,6 +182,9 @@ pub struct Constellation<LTF, STF> { /// Document states for loaded pipelines (used only when writing screenshots). document_states: HashMap<PipelineId, DocumentState>, + + // Webrender interface, if enabled. + webrender_api_sender: Option<webrender_traits::RenderApiSender>, } /// State needed to construct a constellation. @@ -203,6 +207,8 @@ pub struct InitialConstellationState { pub mem_profiler_chan: mem::ProfilerChan, /// Whether the constellation supports the clipboard. pub supports_clipboard: bool, + /// Optional webrender API reference (if enabled). + pub webrender_api_sender: Option<webrender_traits::RenderApiSender>, } /// Stores the navigation context for a single frame in the frame tree. @@ -347,6 +353,7 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF> scheduler_chan: TimerScheduler::start(), child_processes: Vec::new(), document_states: HashMap::new(), + webrender_api_sender: state.webrender_api_sender, }; let namespace_id = constellation.next_pipeline_namespace_id(); PipelineNamespace::install(namespace_id); @@ -399,6 +406,7 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF> load_data: load_data, device_pixel_ratio: self.window_size.device_pixel_ratio, pipeline_namespace_id: self.next_pipeline_namespace_id(), + webrender_api_sender: self.webrender_api_sender.clone(), }); if spawning_paint_only { @@ -1196,7 +1204,9 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF> size: &Size2D<i32>, response_sender: IpcSender<(IpcSender<CanvasMsg>, usize)>) { let id = self.canvas_paint_threads.len(); - let (out_of_process_sender, in_process_sender) = CanvasPaintThread::start(*size); + let webrender_api = self.webrender_api_sender.clone(); + let (out_of_process_sender, in_process_sender) = CanvasPaintThread::start(*size, + webrender_api); self.canvas_paint_threads.push(in_process_sender); response_sender.send((out_of_process_sender, id)).unwrap() } @@ -1206,13 +1216,14 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF> size: &Size2D<i32>, attributes: GLContextAttributes, response_sender: IpcSender<Result<(IpcSender<CanvasMsg>, usize), String>>) { - let response = match WebGLPaintThread::start(*size, attributes) { + let webrender_api = self.webrender_api_sender.clone(); + let response = match WebGLPaintThread::start(*size, attributes, webrender_api) { Ok((out_of_process_sender, in_process_sender)) => { let id = self.webgl_paint_threads.len(); self.webgl_paint_threads.push(in_process_sender); Ok((out_of_process_sender, id)) }, - Err(msg) => Err(msg.to_owned()), + Err(msg) => Err(msg), }; response_sender.send(response).unwrap() diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs index 6434c3a8939..410b007a3a6 100644 --- a/components/compositing/lib.rs +++ b/components/compositing/lib.rs @@ -48,6 +48,8 @@ extern crate time; extern crate url; #[macro_use] extern crate util; +extern crate webrender; +extern crate webrender_traits; pub use compositor_thread::{CompositorEventListener, CompositorProxy, CompositorThread}; pub use constellation::Constellation; diff --git a/components/compositing/pipeline.rs b/components/compositing/pipeline.rs index 2a4e3b1999e..bbf892048c8 100644 --- a/components/compositing/pipeline.rs +++ b/components/compositing/pipeline.rs @@ -35,6 +35,7 @@ use util::geometry::{PagePx, ViewportPx}; use util::ipc::OptionalIpcSender; use util::opts::{self, Opts}; use util::prefs; +use webrender_traits; /// A uniquely-identifiable pipeline of script thread, layout thread, and paint thread. pub struct Pipeline { @@ -113,6 +114,8 @@ pub struct InitialPipelineState { pub load_data: LoadData, /// The ID of the pipeline namespace for this script thread. pub pipeline_namespace_id: PipelineNamespaceId, + /// Optional webrender api (if enabled). + pub webrender_api_sender: Option<webrender_traits::RenderApiSender>, } impl Pipeline { @@ -225,6 +228,7 @@ impl Pipeline { layout_content_process_shutdown_port: layout_content_process_shutdown_port, script_content_process_shutdown_chan: script_content_process_shutdown_chan, script_content_process_shutdown_port: script_content_process_shutdown_port, + webrender_api_sender: state.webrender_api_sender, }; let privileged_pipeline_content = PrivilegedPipelineContent { @@ -376,6 +380,7 @@ pub struct UnprivilegedPipelineContent { layout_content_process_shutdown_port: IpcReceiver<()>, script_content_process_shutdown_chan: IpcSender<()>, script_content_process_shutdown_port: IpcReceiver<()>, + webrender_api_sender: Option<webrender_traits::RenderApiSender>, } impl UnprivilegedPipelineContent { @@ -419,7 +424,8 @@ impl UnprivilegedPipelineContent { self.time_profiler_chan, self.mem_profiler_chan, self.layout_shutdown_chan, - self.layout_content_process_shutdown_chan.clone()); + self.layout_content_process_shutdown_chan.clone(), + self.webrender_api_sender); if wait_for_completion { self.script_content_process_shutdown_port.recv().unwrap(); diff --git a/components/gfx/Cargo.toml b/components/gfx/Cargo.toml index b9e577d3e90..58863a275c1 100644 --- a/components/gfx/Cargo.toml +++ b/components/gfx/Cargo.toml @@ -69,6 +69,9 @@ features = ["plugins"] [dependencies.ipc-channel] git = "https://github.com/servo/ipc-channel" +[dependencies.webrender_traits] +git = "https://github.com/glennw/webrender_traits" + [target.x86_64-apple-darwin.dependencies] core-foundation = "0.2" core-graphics = "0.2" diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index f3aad7848c7..5b0e3935904 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -45,6 +45,7 @@ use util::linked_list::prepend_from; use util::opts; use util::print_tree::PrintTree; use util::range::Range; +use webrender_traits::WebGLContextId; pub use style::dom::OpaqueNode; @@ -641,7 +642,10 @@ impl StackingContext { layer_info: layer_info, last_child_layer_info: None, }; - StackingContextLayerCreator::add_layers_to_preserve_drawing_order(&mut stacking_context); + // webrender doesn't care about layers in the display list - it's handled internally. + if !opts::get().use_webrender { + StackingContextLayerCreator::add_layers_to_preserve_drawing_order(&mut stacking_context); + } stacking_context } @@ -681,7 +685,8 @@ impl StackingContext { // TODO(gw): This is a hack to avoid running the DL optimizer // on 3d transformed tiles. We should have a better solution // than just disabling the opts here. - if paint_context.layer_kind == LayerKind::HasTransform { + if paint_context.layer_kind == LayerKind::HasTransform || + opts::get().use_webrender { // webrender takes care of all culling via aabb tree! self.draw_into_context(&self.display_list, paint_context, &transform, @@ -775,6 +780,9 @@ struct StackingContextLayerCreator { impl StackingContextLayerCreator { fn new() -> StackingContextLayerCreator { + // webrender doesn't care about layers in the display list - it's handled internally. + debug_assert!(!opts::get().use_webrender); + StackingContextLayerCreator { display_list_for_next_layer: None, next_layer_info: None, @@ -969,6 +977,7 @@ pub enum DisplayItem { SolidColorClass(Box<SolidColorDisplayItem>), TextClass(Box<TextDisplayItem>), ImageClass(Box<ImageDisplayItem>), + WebGLClass(Box<WebGLDisplayItem>), BorderClass(Box<BorderDisplayItem>), GradientClass(Box<GradientDisplayItem>), LineClass(Box<LineDisplayItem>), @@ -976,6 +985,7 @@ pub enum DisplayItem { StackingContextClass(Arc<StackingContext>), LayeredItemClass(Box<LayeredItem>), NoopClass(Box<BaseDisplayItem>), + IframeClass(Box<IframeDisplayItem>), } /// Information common to all display items. @@ -1236,6 +1246,20 @@ pub struct ImageDisplayItem { pub image_rendering: image_rendering::T, } +#[derive(Clone, HeapSizeOf, Deserialize, Serialize)] +pub struct WebGLDisplayItem { + pub base: BaseDisplayItem, + #[ignore_heap_size_of = "Defined in webrender_traits"] + pub context_id: WebGLContextId, +} + + +/// Paints an iframe. +#[derive(Clone, HeapSizeOf, Deserialize, Serialize)] +pub struct IframeDisplayItem { + pub base: BaseDisplayItem, + pub iframe: PipelineId, +} /// Paints a gradient. #[derive(Clone, Deserialize, HeapSizeOf, Serialize)] @@ -1450,6 +1474,10 @@ impl DisplayItem { image_item.image_rendering.clone()); } + DisplayItem::WebGLClass(_) => { + panic!("Shouldn't be here, WebGL display items are created just with webrender"); + } + DisplayItem::BorderClass(ref border) => { paint_context.draw_border(&border.base.bounds, &border.border_widths, @@ -1499,6 +1527,7 @@ impl DisplayItem { DisplayItem::LayeredItemClass(_) => panic!("Found layered item during drawing."), DisplayItem::NoopClass(_) => { } + DisplayItem::IframeClass(..) => {} } } @@ -1507,6 +1536,7 @@ impl DisplayItem { DisplayItem::SolidColorClass(ref solid_color) => Some(&solid_color.base), DisplayItem::TextClass(ref text) => Some(&text.base), DisplayItem::ImageClass(ref image_item) => Some(&image_item.base), + DisplayItem::WebGLClass(ref webgl_item) => Some(&webgl_item.base), DisplayItem::BorderClass(ref border) => Some(&border.base), DisplayItem::GradientClass(ref gradient) => Some(&gradient.base), DisplayItem::LineClass(ref line) => Some(&line.base), @@ -1514,6 +1544,7 @@ impl DisplayItem { DisplayItem::LayeredItemClass(ref layered_item) => layered_item.item.base(), DisplayItem::NoopClass(ref base_item) => Some(base_item), DisplayItem::StackingContextClass(_) => None, + DisplayItem::IframeClass(ref iframe) => Some(&iframe.base), } } @@ -1563,6 +1594,7 @@ impl fmt::Debug for DisplayItem { solid_color.color.a), DisplayItem::TextClass(_) => "Text".to_owned(), DisplayItem::ImageClass(_) => "Image".to_owned(), + DisplayItem::WebGLClass(_) => "WebGL".to_owned(), DisplayItem::BorderClass(_) => "Border".to_owned(), DisplayItem::GradientClass(_) => "Gradient".to_owned(), DisplayItem::LineClass(_) => "Line".to_owned(), @@ -1571,6 +1603,7 @@ impl fmt::Debug for DisplayItem { DisplayItem::LayeredItemClass(ref layered_item) => format!("LayeredItem({:?})", layered_item.item), DisplayItem::NoopClass(_) => "Noop".to_owned(), + DisplayItem::IframeClass(_) => "Iframe".to_owned(), }, self.bounds(), ) diff --git a/components/gfx/font.rs b/components/gfx/font.rs index bb9ff7b3d45..8e5a80939ab 100644 --- a/components/gfx/font.rs +++ b/components/gfx/font.rs @@ -23,6 +23,7 @@ use text::shaping::ShaperMethods; use time; use unicode_script::Script; use util::cache::HashCache; +use webrender_traits; static TEXT_SHAPING_PERFORMANCE_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT; @@ -100,6 +101,7 @@ pub struct Font { pub shaper: Option<Shaper>, pub shape_cache: HashCache<ShapeCacheEntry, Arc<GlyphStore>>, pub glyph_advance_cache: HashCache<u32, FractionalPixel>, + pub font_key: Option<webrender_traits::FontKey>, } bitflags! { diff --git a/components/gfx/font_cache_thread.rs b/components/gfx/font_cache_thread.rs index 6a2b5b4a033..89eefe9205b 100644 --- a/components/gfx/font_cache_thread.rs +++ b/components/gfx/font_cache_thread.rs @@ -24,12 +24,19 @@ use url::Url; use util::prefs; use util::str::LowercaseString; use util::thread::spawn_named; +use webrender_traits; /// A list of font templates that make up a given font family. struct FontTemplates { templates: Vec<FontTemplate>, } +#[derive(Serialize, Deserialize, Debug)] +pub struct FontTemplateInfo { + pub font_template: Arc<FontTemplateData>, + pub font_key: Option<webrender_traits::FontKey>, +} + impl FontTemplates { fn new() -> FontTemplates { FontTemplates { @@ -73,7 +80,8 @@ impl FontTemplates { } } - let template = FontTemplate::new(identifier, maybe_data); + let template = FontTemplate::new(identifier, + maybe_data); self.templates.push(template); } } @@ -91,7 +99,7 @@ pub enum Command { /// Reply messages sent from the font cache thread to the FontContext caller. #[derive(Deserialize, Serialize, Debug)] pub enum Reply { - GetFontTemplateReply(Option<Arc<FontTemplateData>>), + GetFontTemplateReply(Option<FontTemplateInfo>), } /// The font cache thread itself. It maintains a list of reference counted @@ -104,6 +112,8 @@ struct FontCache { web_families: HashMap<LowercaseString, FontTemplates>, font_context: FontContextHandle, resource_thread: ResourceThread, + webrender_api: Option<webrender_traits::RenderApi>, + webrender_fonts: HashMap<Atom, webrender_traits::FontKey>, } fn populate_generic_fonts() -> HashMap<FontFamily, LowercaseString> { @@ -285,24 +295,46 @@ impl FontCache { } } + fn get_font_template_info(&mut self, template: Arc<FontTemplateData>) -> FontTemplateInfo { + let webrender_fonts = &mut self.webrender_fonts; + let font_key = self.webrender_api.as_ref().map(|webrender_api| { + *webrender_fonts.entry(template.identifier.clone()).or_insert_with(|| { + match (template.bytes_if_in_memory(), template.native_font()) { + (Some(bytes), _) => webrender_api.add_raw_font(bytes), + (None, Some(native_font)) => webrender_api.add_native_font(native_font), + (None, None) => webrender_api.add_raw_font(template.bytes().clone()), + } + }) + }); + + FontTemplateInfo { + font_template: template, + font_key: font_key, + } + } + fn find_font_template(&mut self, family: &FontFamily, desc: &FontTemplateDescriptor) - -> Option<Arc<FontTemplateData>> { - self.find_font_in_web_family(family, desc) + -> Option<FontTemplateInfo> { + let template = self.find_font_in_web_family(family, desc) .or_else(|| { let transformed_family = self.transform_family(family); self.find_font_in_local_family(&transformed_family, desc) - }) + }); + + template.map(|template| { + self.get_font_template_info(template) + }) } fn last_resort_font_template(&mut self, desc: &FontTemplateDescriptor) - -> Arc<FontTemplateData> { + -> FontTemplateInfo { let last_resort = last_resort_font_families(); for family in &last_resort { let family = LowercaseString::new(family); let maybe_font_in_family = self.find_font_in_local_family(&family, desc); if let Some(family) = maybe_font_in_family { - return family; + return self.get_font_template_info(family) } } @@ -318,7 +350,8 @@ pub struct FontCacheThread { } impl FontCacheThread { - pub fn new(resource_thread: ResourceThread) -> FontCacheThread { + pub fn new(resource_thread: ResourceThread, + webrender_api: Option<webrender_traits::RenderApi>) -> FontCacheThread { let (chan, port) = ipc::channel().unwrap(); let channel_to_self = chan.clone(); @@ -334,6 +367,8 @@ impl FontCacheThread { web_families: HashMap::new(), font_context: FontContextHandle::new(), resource_thread: resource_thread, + webrender_api: webrender_api, + webrender_fonts: HashMap::new(), }; cache.refresh_local_families(); @@ -346,7 +381,7 @@ impl FontCacheThread { } pub fn find_font_template(&self, family: FontFamily, desc: FontTemplateDescriptor) - -> Option<Arc<FontTemplateData>> { + -> Option<FontTemplateInfo> { let (response_chan, response_port) = ipc::channel().unwrap(); self.chan.send(Command::GetFontTemplate(family, desc, response_chan)).unwrap(); @@ -361,7 +396,7 @@ impl FontCacheThread { } pub fn last_resort_font_template(&self, desc: FontTemplateDescriptor) - -> Arc<FontTemplateData> { + -> FontTemplateInfo { let (response_chan, response_port) = ipc::channel().unwrap(); self.chan.send(Command::GetLastResortFontTemplate(desc, response_chan)).unwrap(); diff --git a/components/gfx/font_context.rs b/components/gfx/font_context.rs index 8262b0d15fb..7cb95e6fc54 100644 --- a/components/gfx/font_context.rs +++ b/components/gfx/font_context.rs @@ -28,6 +28,7 @@ use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; use string_cache::Atom; use style::computed_values::{font_style, font_variant}; use util::cache::HashCache; +use webrender_traits; #[cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))] fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: Au) -> ScaledFont { @@ -105,9 +106,12 @@ impl FontContext { } /// Create a font for use in layout calculations. - fn create_layout_font(&self, template: Arc<FontTemplateData>, - descriptor: FontTemplateDescriptor, pt_size: Au, - variant: font_variant::T) -> Result<Font, ()> { + fn create_layout_font(&self, + template: Arc<FontTemplateData>, + descriptor: FontTemplateDescriptor, + pt_size: Au, + variant: font_variant::T, + font_key: Option<webrender_traits::FontKey>) -> Result<Font, ()> { // TODO: (Bug #3463): Currently we only support fake small-caps // painting. We should also support true small-caps (where the // font supports it) in the future. @@ -133,6 +137,7 @@ impl FontContext { metrics: metrics, shape_cache: HashCache::new(), glyph_advance_cache: HashCache::new(), + font_key: font_key, } }) } @@ -201,14 +206,15 @@ impl FontContext { } if !cache_hit { - let font_template = self.font_cache_thread.find_font_template(family.clone(), - desc.clone()); - match font_template { - Some(font_template) => { - let layout_font = self.create_layout_font(font_template, + let template_info = self.font_cache_thread.find_font_template(family.clone(), + desc.clone()); + match template_info { + Some(template_info) => { + let layout_font = self.create_layout_font(template_info.font_template, desc.clone(), style.font_size, - style.font_variant); + style.font_variant, + template_info.font_key); let font = match layout_font { Ok(layout_font) => { let layout_font = Rc::new(RefCell::new(layout_font)); @@ -250,11 +256,12 @@ impl FontContext { } if !cache_hit { - let font_template = self.font_cache_thread.last_resort_font_template(desc.clone()); - let layout_font = self.create_layout_font(font_template, + let template_info = self.font_cache_thread.last_resort_font_template(desc.clone()); + let layout_font = self.create_layout_font(template_info.font_template, desc.clone(), style.font_size, - style.font_variant); + style.font_variant, + template_info.font_key); match layout_font { Ok(layout_font) => { let layout_font = Rc::new(RefCell::new(layout_font)); diff --git a/components/gfx/lib.rs b/components/gfx/lib.rs index 4d17ad41cb1..cb2576dba5c 100644 --- a/components/gfx/lib.rs +++ b/components/gfx/lib.rs @@ -75,6 +75,7 @@ extern crate unicode_script; extern crate url; #[macro_use] extern crate util; +extern crate webrender_traits; pub use paint_context::PaintContext; diff --git a/components/gfx/platform/freetype/font_template.rs b/components/gfx/platform/freetype/font_template.rs index 30f4ffd5437..55a530a7050 100644 --- a/components/gfx/platform/freetype/font_template.rs +++ b/components/gfx/platform/freetype/font_template.rs @@ -5,6 +5,7 @@ use std::fs::File; use std::io::Read; use string_cache::Atom; +use webrender_traits::NativeFontHandle; /// Platform specific font representation for Linux. /// The identifier is an absolute path, and the bytes @@ -36,4 +37,22 @@ impl FontTemplateData { identifier: identifier, } } + + /// Returns a clone of the data in this font. This may be a hugely expensive + /// operation (depending on the platform) which performs synchronous disk I/O + /// and should never be done lightly. + pub fn bytes(&self) -> Vec<u8> { + self.bytes.clone() + } + + /// Returns a clone of the bytes in this font if they are in memory. This function never + /// performs disk I/O. + pub fn bytes_if_in_memory(&self) -> Option<Vec<u8>> { + Some(self.bytes()) + } + + /// Returns the native font that underlies this font template, if applicable. + pub fn native_font(&self) -> Option<NativeFontHandle> { + None + } } diff --git a/components/gfx/platform/macos/font_template.rs b/components/gfx/platform/macos/font_template.rs index d6ee6b9f3f0..f15527e6a9f 100644 --- a/components/gfx/platform/macos/font_template.rs +++ b/components/gfx/platform/macos/font_template.rs @@ -9,9 +9,12 @@ use core_text::font::CTFont; use serde::de::{Error, Visitor}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::borrow::ToOwned; +use std::fs::File; +use std::io::Read; use std::ops::Deref; use std::sync::Mutex; use string_cache::Atom; +use url::Url; /// Platform specific font representation for mac. /// The identifier is a PostScript font name. The @@ -62,6 +65,39 @@ impl FontTemplateData { } ctfont.as_ref().map(|ctfont| (*ctfont).clone()) } + + /// Returns a clone of the data in this font. This may be a hugely expensive + /// operation (depending on the platform) which performs synchronous disk I/O + /// and should never be done lightly. + pub fn bytes(&self) -> Vec<u8> { + match self.bytes_if_in_memory() { + Some(font_data) => return font_data, + None => {} + } + + let path = Url::parse(&*self.ctfont() + .expect("No Core Text font available!") + .url() + .expect("No URL for Core Text font!") + .get_string() + .to_string()).expect("Couldn't parse Core Text font URL!") + .to_file_path() + .expect("Core Text font didn't name a path!"); + let mut bytes = Vec::new(); + File::open(path).expect("Couldn't open font file!").read_to_end(&mut bytes).unwrap(); + bytes + } + + /// Returns a clone of the bytes in this font if they are in memory. This function never + /// performs disk I/O. + pub fn bytes_if_in_memory(&self) -> Option<Vec<u8>> { + self.font_data.clone() + } + + /// Returns the native font that underlies this font template, if applicable. + pub fn native_font(&self) -> Option<CGFont> { + self.ctfont().map(|ctfont| ctfont.copy_to_CGFont()) + } } #[derive(Debug)] diff --git a/components/gfx/text/text_run.rs b/components/gfx/text/text_run.rs index 5b8acb5e1c5..5a3d086de7e 100644 --- a/components/gfx/text/text_run.rs +++ b/components/gfx/text/text_run.rs @@ -13,6 +13,7 @@ use std::sync::Arc; use text::glyph::{CharIndex, GlyphStore}; use util::range::Range; use util::vec::{Comparator, FullBinarySearchMethods}; +use webrender_traits; thread_local! { static INDEX_OF_FIRST_GLYPH_RUN_CACHE: Cell<Option<(*const TextRun, CharIndex, usize)>> = @@ -27,6 +28,7 @@ pub struct TextRun { pub font_template: Arc<FontTemplateData>, pub actual_pt_size: Au, pub font_metrics: FontMetrics, + pub font_key: Option<webrender_traits::FontKey>, /// The glyph runs that make up this text run. pub glyphs: Arc<Vec<GlyphRun>>, pub bidi_level: u8, @@ -177,6 +179,7 @@ impl<'a> TextRun { text: Arc::new(text), font_metrics: font.metrics.clone(), font_template: font.handle.template(), + font_key: font.font_key, actual_pt_size: font.actual_pt_size, glyphs: Arc::new(glyphs), bidi_level: bidi_level, diff --git a/components/layout/Cargo.toml b/components/layout/Cargo.toml index d549967e753..99bf99cd47a 100644 --- a/components/layout/Cargo.toml +++ b/components/layout/Cargo.toml @@ -56,6 +56,9 @@ path = "../util" [dependencies.ipc-channel] git = "https://github.com/servo/ipc-channel" +[dependencies.webrender_traits] +git = "https://github.com/glennw/webrender_traits" + [dependencies] app_units = {version = "0.2.1", features = ["plugins"]} bitflags = "0.3" diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index d49f4f4813b..efd7b0a2394 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -13,7 +13,7 @@ use app_units::{Au, AU_PER_PX}; use azure::azure_hl::Color; use block::BlockFlow; -use canvas_traits::{CanvasMsg, FromLayoutMsg}; +use canvas_traits::{CanvasMsg, CanvasPixelData, CanvasData, FromLayoutMsg}; use context::LayoutContext; use euclid::num::Zero; use euclid::{Matrix4, Point2D, Point3D, Rect, SideOffsets2D, Size2D}; @@ -26,7 +26,7 @@ use gfx::display_list::{BLUR_INFLATION_FACTOR, BaseDisplayItem, BorderDisplayIte use gfx::display_list::{BorderRadii, BoxShadowClipMode, BoxShadowDisplayItem, ClippingRegion}; use gfx::display_list::{DisplayItem, DisplayItemMetadata, DisplayList, DisplayListSection}; use gfx::display_list::{GradientDisplayItem}; -use gfx::display_list::{GradientStop, ImageDisplayItem, LayeredItem, LayerInfo}; +use gfx::display_list::{GradientStop, IframeDisplayItem, ImageDisplayItem, WebGLDisplayItem, LayeredItem, LayerInfo}; use gfx::display_list::{LineDisplayItem, OpaqueNode, SolidColorDisplayItem}; use gfx::display_list::{StackingContext, TextDisplayItem, TextOrientation}; use gfx::paint_thread::THREAD_TINT_COLORS; @@ -51,8 +51,7 @@ use style::properties::style_structs::Border; use style::properties::{self, ComputedValues}; use style::values::RGBA; use style::values::computed; -use style::values::computed::LinearGradient; -use style::values::computed::{LengthOrNone, LengthOrPercentage, LengthOrPercentageOrAuto}; +use style::values::computed::{LengthOrNone, LengthOrPercentage, LengthOrPercentageOrAuto, LinearGradient}; use style::values::specified::{AngleOrCorner, HorizontalDirection, VerticalDirection}; use style_traits::cursor::Cursor; use table_cell::CollapsedBordersForCell; @@ -948,9 +947,12 @@ impl FragmentDisplayListBuilding for Fragment { stacking_relative_flow_origin, self); - if !stacking_relative_border_box.intersects(stacking_relative_display_port) { - debug!("Fragment::build_display_list: outside display port"); - return + // webrender deals with all culling via aabb + if !opts::get().use_webrender { + if !stacking_relative_border_box.intersects(stacking_relative_display_port) { + debug!("Fragment::build_display_list: outside display port"); + return + } } // Calculate the clip rect. If there's nothing to render at all, don't even construct @@ -1112,20 +1114,31 @@ impl FragmentDisplayListBuilding for Fragment { } SpecificFragmentInfo::Iframe(ref fragment_info) => { if !stacking_relative_content_box.is_empty() { - let layer_id = self.layer_id(); - display_list.content.push_back(DisplayItem::LayeredItemClass(box LayeredItem { - item: DisplayItem::NoopClass( - box BaseDisplayItem::new(&stacking_relative_content_box, - DisplayItemMetadata::new(self.node, - &*self.style, - Cursor::DefaultCursor), - clip)), - layer_id: layer_id - })); - - display_list.layer_info.push_back(LayerInfo::new(layer_id, - ScrollPolicy::Scrollable, - Some(fragment_info.pipeline_id))); + if opts::get().use_webrender { + display_list.content.push_back(DisplayItem::IframeClass(box IframeDisplayItem { + base: BaseDisplayItem::new(&stacking_relative_content_box, + DisplayItemMetadata::new(self.node, + &*self.style, + Cursor::DefaultCursor), + clip), + iframe: fragment_info.pipeline_id, + })); + } else { + let layer_id = self.layer_id(); + display_list.content.push_back(DisplayItem::LayeredItemClass(box LayeredItem { + item: DisplayItem::NoopClass( + box BaseDisplayItem::new(&stacking_relative_content_box, + DisplayItemMetadata::new(self.node, + &*self.style, + Cursor::DefaultCursor), + clip)), + layer_id: layer_id + })); + + display_list.layer_info.push_back(LayerInfo::new(layer_id, + ScrollPolicy::Scrollable, + Some(fragment_info.pipeline_id))); + } } } SpecificFragmentInfo::Image(ref mut image_fragment) => { @@ -1144,7 +1157,6 @@ impl FragmentDisplayListBuilding for Fragment { } } SpecificFragmentInfo::Canvas(ref canvas_fragment_info) => { - // TODO(ecoal95): make the canvas with a renderer use the custom layer let width = canvas_fragment_info.replaced_image_fragment_info .computed_inline_size.map_or(0, |w| w.to_px() as usize); let height = canvas_fragment_info.replaced_image_fragment_info @@ -1156,7 +1168,7 @@ impl FragmentDisplayListBuilding for Fragment { let ipc_renderer = ipc_renderer.lock().unwrap(); let (sender, receiver) = ipc::channel().unwrap(); ipc_renderer.send(CanvasMsg::FromLayout( - FromLayoutMsg::SendPixelContents(sender))).unwrap(); + FromLayoutMsg::SendData(sender))).unwrap(); let data = receiver.recv().unwrap(); // Propagate the layer and the renderer to the paint thread. @@ -1165,31 +1177,54 @@ impl FragmentDisplayListBuilding for Fragment { data }, - None => IpcSharedMemory::from_byte(0xFFu8, width * height * 4), - }; - let display_item = DisplayItem::ImageClass(box ImageDisplayItem { - base: BaseDisplayItem::new(&stacking_relative_content_box, - DisplayItemMetadata::new(self.node, - &*self.style, - Cursor::DefaultCursor), - clip), - image: Arc::new(Image { - width: width as u32, - height: height as u32, - format: PixelFormat::RGBA8, - bytes: canvas_data, + None => CanvasData::Pixels(CanvasPixelData { + image_data: IpcSharedMemory::from_byte(0xFFu8, width * height * 4), + image_key: None, }), - stretch_size: stacking_relative_content_box.size, - image_rendering: image_rendering::T::Auto, - }); + }; - display_list.content.push_back(DisplayItem::LayeredItemClass(box LayeredItem { - item: display_item, - layer_id: layer_id - })); + let display_item = match canvas_data { + CanvasData::Pixels(canvas_data) => { + DisplayItem::ImageClass(box ImageDisplayItem { + base: BaseDisplayItem::new(&stacking_relative_content_box, + DisplayItemMetadata::new(self.node, + &*self.style, + Cursor::DefaultCursor), + clip), + image: Arc::new(Image { + width: width as u32, + height: height as u32, + format: PixelFormat::RGBA8, + bytes: canvas_data.image_data, + id: canvas_data.image_key, + }), + stretch_size: stacking_relative_content_box.size, + image_rendering: image_rendering::T::Auto, + }) + } + CanvasData::WebGL(context_id) => { + DisplayItem::WebGLClass(box WebGLDisplayItem { + base: BaseDisplayItem::new(&stacking_relative_content_box, + DisplayItemMetadata::new(self.node, + &*self.style, + Cursor::DefaultCursor), + clip), + context_id: context_id, + }) + } + }; + + if opts::get().use_webrender { + display_list.content.push_back(display_item); + } else { + display_list.content.push_back(DisplayItem::LayeredItemClass(box LayeredItem { + item: display_item, + layer_id: layer_id + })); - display_list.layer_info.push_back( - LayerInfo::new(layer_id, ScrollPolicy::Scrollable, None)); + display_list.layer_info.push_back( + LayerInfo::new(layer_id, ScrollPolicy::Scrollable, None)); + } } } SpecificFragmentInfo::UnscannedText(_) => { @@ -1993,4 +2028,3 @@ pub enum StackingContextCreationMode { OuterScrollWrapper, InnerScrollWrapper, } - diff --git a/components/layout/layout_thread.rs b/components/layout/layout_thread.rs index e5f72a395e4..fbc1cd26daf 100644 --- a/components/layout/layout_thread.rs +++ b/components/layout/layout_thread.rs @@ -35,7 +35,7 @@ use ipc_channel::router::ROUTER; use layout_debug; use layout_traits::LayoutThreadFactory; use log; -use msg::constellation_msg::{ConstellationChan, Failure, PipelineId}; +use msg::constellation_msg::{ConstellationChan, ConvertPipelineIdToWebRender, Failure, PipelineId}; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread}; use parallel; use profile_traits::mem::{self, Report, ReportKind, ReportsChan}; @@ -80,6 +80,8 @@ use util::opts; use util::thread; use util::thread_state; use util::workqueue::WorkQueue; +use webrender_helpers::WebRenderStackingContextConverter; +use webrender_traits; use wrapper::{LayoutNode, NonOpaqueStyleAndLayoutData, ServoLayoutNode, ThreadSafeLayoutNode}; /// The number of screens of data we're allowed to generate display lists for in each direction. @@ -221,6 +223,8 @@ pub struct LayoutThread { /// The CSS error reporter for all CSS loaded in this layout thread error_reporter: CSSErrorReporter, + // Webrender interface, if enabled. + webrender_api: Option<webrender_traits::RenderApi>, } impl LayoutThreadFactory for LayoutThread { @@ -240,7 +244,8 @@ impl LayoutThreadFactory for LayoutThread { time_profiler_chan: time::ProfilerChan, mem_profiler_chan: mem::ProfilerChan, shutdown_chan: IpcSender<()>, - content_process_shutdown_chan: IpcSender<()>) { + content_process_shutdown_chan: IpcSender<()>, + webrender_api_sender: Option<webrender_traits::RenderApiSender>) { let ConstellationChan(con_chan) = constellation_chan.clone(); thread::spawn_named_with_send_on_failure(format!("LayoutThread {:?}", id), thread_state::LAYOUT, @@ -258,7 +263,8 @@ impl LayoutThreadFactory for LayoutThread { image_cache_thread, font_cache_thread, time_profiler_chan, - mem_profiler_chan.clone()); + mem_profiler_chan.clone(), + webrender_api_sender); let reporter_name = format!("layout-reporter-{}", id); mem_profiler_chan.run_with_memory_reporting(|| { @@ -367,7 +373,8 @@ impl LayoutThread { image_cache_thread: ImageCacheThread, font_cache_thread: FontCacheThread, time_profiler_chan: time::ProfilerChan, - mem_profiler_chan: mem::ProfilerChan) + mem_profiler_chan: mem::ProfilerChan, + webrender_api_sender: Option<webrender_traits::RenderApiSender>) -> LayoutThread { let device = Device::new( MediaType::Screen, @@ -437,6 +444,7 @@ impl LayoutThread { expired_animations: Arc::new(RwLock::new(HashMap::new())), epoch: Epoch(0), viewport_size: Size2D::new(Au(0), Au(0)), + webrender_api: webrender_api_sender.map(|wr| wr.create_api()), rw_data: Arc::new(Mutex::new( LayoutThreadData { constellation_chan: constellation_chan, @@ -705,7 +713,8 @@ impl LayoutThread { self.time_profiler_chan.clone(), self.mem_profiler_chan.clone(), info.layout_shutdown_chan, - info.content_process_shutdown_chan); + info.content_process_shutdown_chan, + self.webrender_api.as_ref().map(|wr| wr.clone_sender())); } /// Enters a quiescent state in which no new messages will be processed until an `ExitNow` is @@ -908,9 +917,40 @@ impl LayoutThread { debug!("Layout done!"); self.epoch.next(); - self.paint_chan - .send(LayoutToPaintMsg::PaintInit(self.epoch, paint_layer)) - .unwrap(); + + if opts::get().use_webrender { + let api = self.webrender_api.as_ref().unwrap(); + // TODO: Avoid the temporary conversion and build webrender sc/dl directly! + let Epoch(epoch_number) = self.epoch; + let epoch = webrender_traits::Epoch(epoch_number); + let pipeline_id = self.id.to_webrender(); + + // TODO(gw) For now only create a root scrolling layer! + let root_scroll_layer_id = webrender_traits::ScrollLayerId::new(pipeline_id, 0); + let sc_id = rw_data.stacking_context.as_ref() + .unwrap() + .convert_to_webrender(&self.webrender_api.as_ref().unwrap(), + pipeline_id, + epoch, + Some(root_scroll_layer_id)); + let root_background_color = webrender_traits::ColorF::new(root_background_color.r, + root_background_color.g, + root_background_color.b, + root_background_color.a); + + let viewport_size = Size2D::new(self.viewport_size.width.to_f32_px(), + self.viewport_size.height.to_f32_px()); + + api.set_root_stacking_context(sc_id, + root_background_color, + epoch, + pipeline_id, + viewport_size); + } else { + self.paint_chan + .send(LayoutToPaintMsg::PaintInit(self.epoch, paint_layer)) + .unwrap(); + } } }); } diff --git a/components/layout/lib.rs b/components/layout/lib.rs index 498e91fb206..431f576ca78 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -59,6 +59,7 @@ extern crate unicode_script; extern crate url; #[macro_use] extern crate util; +extern crate webrender_traits; #[macro_use] mod layout_debug; @@ -95,4 +96,5 @@ mod table_rowgroup; mod table_wrapper; mod text; mod traversal; +mod webrender_helpers; mod wrapper; diff --git a/components/layout/webrender_helpers.rs b/components/layout/webrender_helpers.rs new file mode 100644 index 00000000000..a1ab8fac3fe --- /dev/null +++ b/components/layout/webrender_helpers.rs @@ -0,0 +1,481 @@ +/* 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/. */ + +// TODO(gw): This contains helper traits and implementations for converting Servo display lists +// into WebRender display lists. In the future, this step should be completely removed. +// This might be achieved by sharing types between WR and Servo display lists, or +// completely converting layout to directly generate WebRender display lists, for example. + +use app_units::Au; +use azure::azure_hl::Color; +use euclid::num::Zero; +use euclid::{Point2D, Rect, Size2D}; +use gfx::display_list::{BorderRadii, BoxShadowClipMode, ClippingRegion}; +use gfx::display_list::{DisplayItem, DisplayList}; +use gfx::display_list::{GradientStop, StackingContext}; +use gfx_traits::ScrollPolicy; +use msg::constellation_msg::ConvertPipelineIdToWebRender; +use style::computed_values::filter::{self, Filter}; +use style::computed_values::{image_rendering, mix_blend_mode}; +use style::values::computed::BorderStyle; +use webrender_traits; + +pub trait WebRenderStackingContextConverter { + fn convert_to_webrender(&self, + api: &webrender_traits::RenderApi, + pipeline_id: webrender_traits::PipelineId, + epoch: webrender_traits::Epoch, + scroll_layer_id: Option<webrender_traits::ScrollLayerId>) + -> webrender_traits::StackingContextId; +} + +trait WebRenderDisplayListConverter { + fn convert_to_webrender(&self, + api: &webrender_traits::RenderApi, + pipeline_id: webrender_traits::PipelineId, + epoch: webrender_traits::Epoch) -> webrender_traits::DisplayListBuilder; +} + +trait WebRenderDisplayItemConverter { + fn convert_to_webrender(&self, + api: &webrender_traits::RenderApi, + pipeline_id: webrender_traits::PipelineId, + epoch: webrender_traits::Epoch, + level: webrender_traits::StackingLevel, + builder: &mut webrender_traits::DisplayListBuilder); +} + +trait ToBorderStyle { + fn to_border_style(&self) -> webrender_traits::BorderStyle; +} + +impl ToBorderStyle for BorderStyle { + fn to_border_style(&self) -> webrender_traits::BorderStyle { + match *self { + BorderStyle::none => webrender_traits::BorderStyle::None, + BorderStyle::solid => webrender_traits::BorderStyle::Solid, + BorderStyle::double => webrender_traits::BorderStyle::Double, + BorderStyle::dotted => webrender_traits::BorderStyle::Dotted, + BorderStyle::dashed => webrender_traits::BorderStyle::Dashed, + BorderStyle::hidden => webrender_traits::BorderStyle::Hidden, + BorderStyle::groove => webrender_traits::BorderStyle::Groove, + BorderStyle::ridge => webrender_traits::BorderStyle::Ridge, + BorderStyle::inset => webrender_traits::BorderStyle::Inset, + BorderStyle::outset => webrender_traits::BorderStyle::Outset, + } + } +} + +trait ToBoxShadowClipMode { + fn to_clip_mode(&self) -> webrender_traits::BoxShadowClipMode; +} + +impl ToBoxShadowClipMode for BoxShadowClipMode { + fn to_clip_mode(&self) -> webrender_traits::BoxShadowClipMode { + match *self { + BoxShadowClipMode::None => webrender_traits::BoxShadowClipMode::None, + BoxShadowClipMode::Inset => webrender_traits::BoxShadowClipMode::Inset, + BoxShadowClipMode::Outset => webrender_traits::BoxShadowClipMode::Outset, + } + } +} + +trait ToSizeF { + fn to_sizef(&self) -> Size2D<f32>; +} + +trait ToPointF { + fn to_pointf(&self) -> Point2D<f32>; +} + +impl ToPointF for Point2D<Au> { + fn to_pointf(&self) -> Point2D<f32> { + Point2D::new(self.x.to_f32_px(), self.y.to_f32_px()) + } +} + +impl ToSizeF for Size2D<Au> { + fn to_sizef(&self) -> Size2D<f32> { + Size2D::new(self.width.to_f32_px(), self.height.to_f32_px()) + } +} + +trait ToRectF { + fn to_rectf(&self) -> Rect<f32>; +} + +impl ToRectF for Rect<Au> { + fn to_rectf(&self) -> Rect<f32> { + let x = self.origin.x.to_f32_px(); + let y = self.origin.y.to_f32_px(); + let w = self.size.width.to_f32_px(); + let h = self.size.height.to_f32_px(); + Rect::new(Point2D::new(x, y), Size2D::new(w, h)) + } +} + +trait ToColorF { + fn to_colorf(&self) -> webrender_traits::ColorF; +} + +impl ToColorF for Color { + fn to_colorf(&self) -> webrender_traits::ColorF { + webrender_traits::ColorF::new(self.r, self.g, self.b, self.a) + } +} + +trait ToGradientStop { + fn to_gradient_stop(&self) -> webrender_traits::GradientStop; +} + +impl ToGradientStop for GradientStop { + fn to_gradient_stop(&self) -> webrender_traits::GradientStop { + webrender_traits::GradientStop { + offset: self.offset, + color: self.color.to_colorf(), + } + } +} + +trait ToClipRegion { + fn to_clip_region(&self) -> webrender_traits::ClipRegion; +} + +impl ToClipRegion for ClippingRegion { + fn to_clip_region(&self) -> webrender_traits::ClipRegion { + webrender_traits::ClipRegion::new(self.main.to_rectf(), + self.complex.iter().map(|complex_clipping_region| { + webrender_traits::ComplexClipRegion::new( + complex_clipping_region.rect.to_rectf(), + complex_clipping_region.radii.to_border_radius(), + ) + }).collect()) + } +} + +trait ToBorderRadius { + fn to_border_radius(&self) -> webrender_traits::BorderRadius; +} + +impl ToBorderRadius for BorderRadii<Au> { + fn to_border_radius(&self) -> webrender_traits::BorderRadius { + webrender_traits::BorderRadius { + top_left: self.top_left.to_sizef(), + top_right: self.top_right.to_sizef(), + bottom_left: self.bottom_left.to_sizef(), + bottom_right: self.bottom_right.to_sizef(), + } + } +} + +trait ToBlendMode { + fn to_blend_mode(&self) -> webrender_traits::MixBlendMode; +} + +impl ToBlendMode for mix_blend_mode::T { + fn to_blend_mode(&self) -> webrender_traits::MixBlendMode { + match *self { + mix_blend_mode::T::normal => webrender_traits::MixBlendMode::Normal, + mix_blend_mode::T::multiply => webrender_traits::MixBlendMode::Multiply, + mix_blend_mode::T::screen => webrender_traits::MixBlendMode::Screen, + mix_blend_mode::T::overlay => webrender_traits::MixBlendMode::Overlay, + mix_blend_mode::T::darken => webrender_traits::MixBlendMode::Darken, + mix_blend_mode::T::lighten => webrender_traits::MixBlendMode::Lighten, + mix_blend_mode::T::color_dodge => webrender_traits::MixBlendMode::ColorDodge, + mix_blend_mode::T::color_burn => webrender_traits::MixBlendMode::ColorBurn, + mix_blend_mode::T::hard_light => webrender_traits::MixBlendMode::HardLight, + mix_blend_mode::T::soft_light => webrender_traits::MixBlendMode::SoftLight, + mix_blend_mode::T::difference => webrender_traits::MixBlendMode::Difference, + mix_blend_mode::T::exclusion => webrender_traits::MixBlendMode::Exclusion, + mix_blend_mode::T::hue => webrender_traits::MixBlendMode::Hue, + mix_blend_mode::T::saturation => webrender_traits::MixBlendMode::Saturation, + mix_blend_mode::T::color => webrender_traits::MixBlendMode::Color, + mix_blend_mode::T::luminosity => webrender_traits::MixBlendMode::Luminosity, + } + } +} + +trait ToImageRendering { + fn to_image_rendering(&self) -> webrender_traits::ImageRendering; +} + +impl ToImageRendering for image_rendering::T { + fn to_image_rendering(&self) -> webrender_traits::ImageRendering { + match *self { + image_rendering::T::CrispEdges => webrender_traits::ImageRendering::CrispEdges, + image_rendering::T::Auto => webrender_traits::ImageRendering::Auto, + image_rendering::T::Pixelated => webrender_traits::ImageRendering::Pixelated, + } + } +} + +trait ToFilterOps { + fn to_filter_ops(&self) -> Vec<webrender_traits::FilterOp>; +} + +impl ToFilterOps for filter::T { + fn to_filter_ops(&self) -> Vec<webrender_traits::FilterOp> { + let mut result = Vec::with_capacity(self.filters.len()); + for filter in self.filters.iter() { + match *filter { + Filter::Blur(radius) => result.push(webrender_traits::FilterOp::Blur(radius)), + Filter::Brightness(amount) => result.push(webrender_traits::FilterOp::Brightness(amount)), + Filter::Contrast(amount) => result.push(webrender_traits::FilterOp::Contrast(amount)), + Filter::Grayscale(amount) => result.push(webrender_traits::FilterOp::Grayscale(amount)), + Filter::HueRotate(angle) => result.push(webrender_traits::FilterOp::HueRotate(angle.0)), + Filter::Invert(amount) => result.push(webrender_traits::FilterOp::Invert(amount)), + Filter::Opacity(amount) => result.push(webrender_traits::FilterOp::Opacity(amount)), + Filter::Saturate(amount) => result.push(webrender_traits::FilterOp::Saturate(amount)), + Filter::Sepia(amount) => result.push(webrender_traits::FilterOp::Sepia(amount)), + } + } + result + } +} + +impl WebRenderStackingContextConverter for StackingContext { + fn convert_to_webrender(&self, + api: &webrender_traits::RenderApi, + pipeline_id: webrender_traits::PipelineId, + epoch: webrender_traits::Epoch, + scroll_layer_id: Option<webrender_traits::ScrollLayerId>) + -> webrender_traits::StackingContextId { + let scroll_policy = self.layer_info + .map_or(webrender_traits::ScrollPolicy::Scrollable, |info| { + match info.scroll_policy { + ScrollPolicy::Scrollable => webrender_traits::ScrollPolicy::Scrollable, + ScrollPolicy::FixedPosition => webrender_traits::ScrollPolicy::Fixed, + } + }); + + let mut sc = webrender_traits::StackingContext::new(scroll_layer_id, + scroll_policy, + self.bounds.to_rectf(), + self.overflow.to_rectf(), + self.z_index, + &self.transform, + &self.perspective, + self.establishes_3d_context, + self.blend_mode.to_blend_mode(), + self.filters.to_filter_ops()); + + let dl_builder = self.display_list.convert_to_webrender(api, + pipeline_id, + epoch); + api.add_display_list(dl_builder, &mut sc, pipeline_id, epoch); + + api.add_stacking_context(sc, pipeline_id, epoch) + } +} + +impl WebRenderDisplayListConverter for Box<DisplayList> { + fn convert_to_webrender(&self, + api: &webrender_traits::RenderApi, + pipeline_id: webrender_traits::PipelineId, + epoch: webrender_traits::Epoch) -> webrender_traits::DisplayListBuilder { + let mut builder = webrender_traits::DisplayListBuilder::new(); + + for item in &self.background_and_borders { + item.convert_to_webrender(api, + pipeline_id, + epoch, + webrender_traits::StackingLevel::BackgroundAndBorders, + &mut builder); + } + + for item in &self.block_backgrounds_and_borders { + item.convert_to_webrender(api, + pipeline_id, + epoch, + webrender_traits::StackingLevel::BlockBackgroundAndBorders, + &mut builder); + } + + for item in &self.floats { + item.convert_to_webrender(api, + pipeline_id, + epoch, + webrender_traits::StackingLevel::Floats, + &mut builder); + } + + for item in &self.content { + item.convert_to_webrender(api, + pipeline_id, + epoch, + webrender_traits::StackingLevel::Content, + &mut builder); + } + + for item in &self.positioned_content { + item.convert_to_webrender(api, + pipeline_id, + epoch, + webrender_traits::StackingLevel::PositionedContent, + &mut builder); + } + + for item in &self.outlines { + item.convert_to_webrender(api, + pipeline_id, + epoch, + webrender_traits::StackingLevel::Outlines, + &mut builder); + } + + builder + } +} + +impl WebRenderDisplayItemConverter for DisplayItem { + fn convert_to_webrender(&self, + api: &webrender_traits::RenderApi, + pipeline_id: webrender_traits::PipelineId, + epoch: webrender_traits::Epoch, + level: webrender_traits::StackingLevel, + builder: &mut webrender_traits::DisplayListBuilder) { + match *self { + DisplayItem::SolidColorClass(ref item) => { + let color = item.color.to_colorf(); + if color.a > 0.0 { + builder.push_rect(level, + item.base.bounds.to_rectf(), + item.base.clip.to_clip_region(), + color); + } + } + DisplayItem::TextClass(ref item) => { + let mut origin = item.baseline_origin.clone(); + let mut glyphs = vec!(); + + for slice in item.text_run.natural_word_slices_in_visual_order(&item.range) { + for glyph in slice.glyphs.iter_glyphs_for_char_range(&slice.range) { + let glyph_advance = glyph.advance(); + let glyph_offset = glyph.offset().unwrap_or(Point2D::zero()); + let glyph = webrender_traits::GlyphInstance { + index: glyph.id(), + x: (origin.x + glyph_offset.x).to_f32_px(), + y: (origin.y + glyph_offset.y).to_f32_px(), + }; + origin = Point2D::new(origin.x + glyph_advance, origin.y); + glyphs.push(glyph); + }; + } + + if glyphs.len() > 0 { + builder.push_text(level, + item.base.bounds.to_rectf(), + item.base.clip.to_clip_region(), + glyphs, + item.text_run.font_key.expect("Font not added to webrender!"), + item.text_color.to_colorf(), + item.text_run.actual_pt_size, + item.blur_radius); + } + } + DisplayItem::ImageClass(ref item) => { + if let Some(id) = item.image.id { + if item.stretch_size.width > Au(0) && + item.stretch_size.height > Au(0) { + builder.push_image(level, + item.base.bounds.to_rectf(), + item.base.clip.to_clip_region(), + item.stretch_size.to_sizef(), + item.image_rendering.to_image_rendering(), + id); + } + } + } + DisplayItem::WebGLClass(ref item) => { + builder.push_webgl_canvas(level, + item.base.bounds.to_rectf(), + item.base.clip.to_clip_region(), + item.context_id); + } + DisplayItem::BorderClass(ref item) => { + let rect = item.base.bounds.to_rectf(); + let left = webrender_traits::BorderSide { + width: item.border_widths.left.to_f32_px(), + color: item.color.left.to_colorf(), + style: item.style.left.to_border_style(), + }; + let top = webrender_traits::BorderSide { + width: item.border_widths.top.to_f32_px(), + color: item.color.top.to_colorf(), + style: item.style.top.to_border_style(), + }; + let right = webrender_traits::BorderSide { + width: item.border_widths.right.to_f32_px(), + color: item.color.right.to_colorf(), + style: item.style.right.to_border_style(), + }; + let bottom = webrender_traits::BorderSide { + width: item.border_widths.bottom.to_f32_px(), + color: item.color.bottom.to_colorf(), + style: item.style.bottom.to_border_style(), + }; + let radius = item.radius.to_border_radius(); + builder.push_border(level, + rect, + item.base.clip.to_clip_region(), + left, + top, + right, + bottom, + radius); + } + DisplayItem::GradientClass(ref item) => { + let rect = item.base.bounds.to_rectf(); + let start_point = item.start_point.to_pointf(); + let end_point = item.end_point.to_pointf(); + let mut stops = Vec::new(); + for stop in &item.stops { + stops.push(stop.to_gradient_stop()); + } + builder.push_gradient(level, + rect, + item.base.clip.to_clip_region(), + start_point, + end_point, + stops); + } + DisplayItem::LineClass(..) => { + println!("TODO DisplayItem::LineClass"); + } + DisplayItem::LayeredItemClass(..) | + DisplayItem::NoopClass(..) => { + panic!("Unexpected in webrender!"); + } + DisplayItem::BoxShadowClass(ref item) => { + let rect = item.base.bounds.to_rectf(); + let box_bounds = item.box_bounds.to_rectf(); + builder.push_box_shadow(level, + rect, + item.base.clip.to_clip_region(), + box_bounds, + item.offset.to_pointf(), + item.color.to_colorf(), + item.blur_radius.to_f32_px(), + item.spread_radius.to_f32_px(), + item.border_radius.to_f32_px(), + item.clip_mode.to_clip_mode()); + } + DisplayItem::IframeClass(ref item) => { + let rect = item.base.bounds.to_rectf(); + let pipeline_id = item.iframe.to_webrender(); + builder.push_iframe(level, + rect, + item.base.clip.to_clip_region(), + pipeline_id); + } + DisplayItem::StackingContextClass(ref item) => { + let stacking_context_id = item.convert_to_webrender(api, + pipeline_id, + epoch, + None); + builder.push_stacking_context(level, stacking_context_id); + } + } + } +} diff --git a/components/layout_traits/Cargo.toml b/components/layout_traits/Cargo.toml index 33353c52773..1afb7d9b920 100644 --- a/components/layout_traits/Cargo.toml +++ b/components/layout_traits/Cargo.toml @@ -28,6 +28,9 @@ path = "../util" [dependencies.ipc-channel] git = "https://github.com/servo/ipc-channel" +[dependencies.webrender_traits] +git = "https://github.com/glennw/webrender_traits" + [dependencies] serde = "0.6" serde_macros = "0.6" diff --git a/components/layout_traits/lib.rs b/components/layout_traits/lib.rs index e5b39a1db4b..2be15566199 100644 --- a/components/layout_traits/lib.rs +++ b/components/layout_traits/lib.rs @@ -14,6 +14,7 @@ extern crate script_traits; extern crate serde; extern crate url; extern crate util; +extern crate webrender_traits; // This module contains traits in layout used generically // in the rest of Servo. @@ -54,5 +55,6 @@ pub trait LayoutThreadFactory { time_profiler_chan: time::ProfilerChan, mem_profiler_chan: mem::ProfilerChan, shutdown_chan: IpcSender<()>, - content_process_shutdown_chan: IpcSender<()>); + content_process_shutdown_chan: IpcSender<()>, + webrender_api_sender: Option<webrender_traits::RenderApiSender>); } diff --git a/components/msg/Cargo.toml b/components/msg/Cargo.toml index b96bdeb953a..05f84756a5f 100644 --- a/components/msg/Cargo.toml +++ b/components/msg/Cargo.toml @@ -20,6 +20,9 @@ git = "https://github.com/servo/ipc-channel" [dependencies.plugins] path = "../plugins" +[dependencies.webrender_traits] +git = "https://github.com/glennw/webrender_traits" + [dependencies] bitflags = "0.3" cssparser = {version = "0.5.3", features = ["heap_size", "serde-serialization"]} diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index adefb76010c..83f308b26fd 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -17,6 +17,7 @@ use std::fmt; use url::Url; use util::geometry::{PagePx, ViewportPx}; use webdriver_msg::{LoadStatus, WebDriverScriptCommand}; +use webrender_traits; #[derive(Deserialize, Serialize)] pub struct ConstellationChan<T: Deserialize + Serialize>(pub IpcSender<T>); @@ -207,7 +208,7 @@ pub enum WebDriverCommandMsg { TakeScreenshot(PipelineId, IpcSender<Option<Image>>), } -#[derive(Deserialize, Eq, PartialEq, Serialize, HeapSizeOf)] +#[derive(Clone, Copy, Deserialize, Eq, PartialEq, Serialize, HeapSizeOf)] pub enum PixelFormat { K8, // Luminance channel only KA8, // Luminance + alpha @@ -228,6 +229,8 @@ pub struct Image { pub format: PixelFormat, #[ignore_heap_size_of = "Defined in ipc-channel"] pub bytes: IpcSharedMemory, + #[ignore_heap_size_of = "Defined in webrender_traits"] + pub id: Option<webrender_traits::ImageKey>, } /// Similar to net::resource_thread::LoadData @@ -353,3 +356,28 @@ impl fmt::Display for PipelineId { #[derive(Clone, PartialEq, Eq, Copy, Hash, Debug, Deserialize, Serialize, HeapSizeOf)] pub struct SubpageId(pub u32); + +pub trait ConvertPipelineIdToWebRender { + fn to_webrender(&self) -> webrender_traits::PipelineId; +} + +pub trait ConvertPipelineIdFromWebRender { + fn from_webrender(&self) -> PipelineId; +} + +impl ConvertPipelineIdToWebRender for PipelineId { + fn to_webrender(&self) -> webrender_traits::PipelineId { + let PipelineNamespaceId(namespace_id) = self.namespace_id; + let PipelineIndex(index) = self.index; + webrender_traits::PipelineId(namespace_id, index) + } +} + +impl ConvertPipelineIdFromWebRender for webrender_traits::PipelineId { + fn from_webrender(&self) -> PipelineId { + PipelineId { + namespace_id: PipelineNamespaceId(self.0), + index: PipelineIndex(self.1), + } + } +} diff --git a/components/msg/lib.rs b/components/msg/lib.rs index febf188fd77..93a513ef742 100644 --- a/components/msg/lib.rs +++ b/components/msg/lib.rs @@ -16,6 +16,7 @@ extern crate rustc_serialize; extern crate serde; extern crate url; extern crate util; +extern crate webrender_traits; pub mod constellation_msg; pub mod webdriver_msg; diff --git a/components/net/Cargo.toml b/components/net/Cargo.toml index 5aef635d0fe..ae03f560029 100644 --- a/components/net/Cargo.toml +++ b/components/net/Cargo.toml @@ -28,6 +28,9 @@ path = "../msg" [dependencies.ipc-channel] git = "https://github.com/servo/ipc-channel" +[dependencies.webrender_traits] +git = "https://github.com/glennw/webrender_traits" + [dependencies] hyper = { version = "0.7", features = [ "serde-serialization" ] } log = "0.3" diff --git a/components/net/image_cache_thread.rs b/components/net/image_cache_thread.rs index e7d960068e0..22797bda379 100644 --- a/components/net/image_cache_thread.rs +++ b/components/net/image_cache_thread.rs @@ -5,7 +5,7 @@ use immeta::load_from_buf; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; -use net_traits::image::base::{Image, ImageMetadata, load_from_memory}; +use net_traits::image::base::{Image, ImageMetadata, load_from_memory, PixelFormat}; use net_traits::image_cache_thread::ImageResponder; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheCommand, ImageCacheThread, ImageState}; use net_traits::image_cache_thread::{ImageCacheResult, ImageOrMetadataAvailable, ImageResponse, UsePlaceholder}; @@ -23,6 +23,7 @@ use url::Url; use util::resource_files::resources_dir_path; use util::thread::spawn_named; use util::threadpool::ThreadPool; +use webrender_traits; /// /// TODO(gw): Remaining work on image cache: @@ -51,6 +52,12 @@ struct PendingLoad { url: Arc<Url> } +enum LoadResult { + Loaded(Image), + PlaceholderLoaded(Arc<Image>), + None +} + impl PendingLoad { fn new(url: Arc<Url>) -> PendingLoad { PendingLoad { @@ -250,6 +257,9 @@ struct ImageCache { // The placeholder image used when an image fails to load placeholder_image: Option<Arc<Image>>, + + // Webrender API instance, if enabled. + webrender_api: Option<webrender_traits::RenderApi>, } /// Message that the decoder worker threads send to main image cache thread. @@ -265,6 +275,16 @@ enum SelectResult { Decoder(DecoderMsg), } +fn convert_format(format: PixelFormat) -> webrender_traits::ImageFormat { + match format { + PixelFormat::K8 | PixelFormat::KA8 => { + panic!("Not support by webrender yet"); + } + PixelFormat::RGB8 => webrender_traits::ImageFormat::RGB8, + PixelFormat::RGBA8 => webrender_traits::ImageFormat::RGBA8, + } +} + impl ImageCache { fn run(&mut self) { let mut exit_sender: Option<IpcSender<()>> = None; @@ -381,10 +401,10 @@ impl ImageCache { Err(_) => { match self.placeholder_image.clone() { Some(placeholder_image) => { - self.complete_load(msg.key, ImageResponse::PlaceholderLoaded( + self.complete_load(msg.key, LoadResult::PlaceholderLoaded( placeholder_image)) } - None => self.complete_load(msg.key, ImageResponse::None), + None => self.complete_load(msg.key, LoadResult::None), } } } @@ -395,16 +415,34 @@ impl ImageCache { // Handle a message from one of the decoder worker threads fn handle_decoder(&mut self, msg: DecoderMsg) { let image = match msg.image { - None => ImageResponse::None, - Some(image) => ImageResponse::Loaded(Arc::new(image)), + None => LoadResult::None, + Some(image) => LoadResult::Loaded(image), }; self.complete_load(msg.key, image); } // Change state of a url from pending -> loaded. - fn complete_load(&mut self, key: LoadKey, image_response: ImageResponse) { + fn complete_load(&mut self, key: LoadKey, mut load_result: LoadResult) { let pending_load = self.pending_loads.remove(&key).unwrap(); + if let Some(ref webrender_api) = self.webrender_api { + 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); + image.id = Some(webrender_api.add_image(image.width, image.height, format, bytes)); + } + LoadResult::PlaceholderLoaded(..) | LoadResult::None => {} + } + } + + let image_response = match load_result { + LoadResult::Loaded(image) => ImageResponse::Loaded(Arc::new(image)), + LoadResult::PlaceholderLoaded(image) => ImageResponse::PlaceholderLoaded(image), + LoadResult::None => ImageResponse::None, + }; + let completed_load = CompletedLoad::new(image_response.clone()); self.completed_loads.insert(pending_load.url, completed_load); @@ -510,7 +548,8 @@ impl ImageCache { } /// Create a new image cache. -pub fn new_image_cache_thread(resource_thread: ResourceThread) -> ImageCacheThread { +pub fn new_image_cache_thread(resource_thread: ResourceThread, + webrender_api: Option<webrender_traits::RenderApi>) -> ImageCacheThread { let (ipc_command_sender, ipc_command_receiver) = ipc::channel().unwrap(); let (progress_sender, progress_receiver) = channel(); let (decoder_sender, decoder_receiver) = channel(); @@ -526,7 +565,14 @@ pub fn new_image_cache_thread(resource_thread: ResourceThread) -> ImageCacheThre file.read_to_end(&mut image_data) }); let placeholder_image = result.ok().map(|_| { - Arc::new(load_from_memory(&image_data).unwrap()) + let mut image = load_from_memory(&image_data).unwrap(); + if let Some(ref webrender_api) = webrender_api { + let format = convert_format(image.format); + let mut bytes = Vec::new(); + bytes.extend_from_slice(&*image.bytes); + image.id = Some(webrender_api.add_image(image.width, image.height, format, bytes)); + } + Arc::new(image) }); // Ask the router to proxy messages received over IPC to us. @@ -543,6 +589,7 @@ pub fn new_image_cache_thread(resource_thread: ResourceThread) -> ImageCacheThre completed_loads: HashMap::new(), resource_thread: resource_thread, placeholder_image: placeholder_image, + webrender_api: webrender_api, }; cache.run(); diff --git a/components/net/lib.rs b/components/net/lib.rs index 143672962cb..fa354bfc439 100644 --- a/components/net/lib.rs +++ b/components/net/lib.rs @@ -27,6 +27,7 @@ extern crate time; extern crate url; extern crate util; extern crate uuid; +extern crate webrender_traits; extern crate websocket; pub mod about_loader; diff --git a/components/net_traits/image/base.rs b/components/net_traits/image/base.rs index 7faf439e34b..84612aae52b 100644 --- a/components/net_traits/image/base.rs +++ b/components/net_traits/image/base.rs @@ -5,6 +5,7 @@ use ipc_channel::ipc::IpcSharedMemory; use piston_image::{self, DynamicImage, GenericImage, ImageFormat}; use stb_image::image as stb_image2; +use util::opts; use util::vec::byte_swap; pub use msg::constellation_msg::{Image, ImageMetadata, PixelFormat}; @@ -15,14 +16,25 @@ pub use msg::constellation_msg::{Image, ImageMetadata, PixelFormat}; // TODO(pcwalton): Speed up with SIMD, or better yet, find some way to not do this. fn byte_swap_and_premultiply(data: &mut [u8]) { let length = data.len(); + + // No need to pre-multiply alpha when using direct GPU rendering. + let premultiply_alpha = !opts::get().use_webrender; + for i in (0..length).step_by(4) { let r = data[i + 2]; let g = data[i + 1]; let b = data[i + 0]; let a = data[i + 3]; - data[i + 0] = ((r as u32) * (a as u32) / 255) as u8; - data[i + 1] = ((g as u32) * (a as u32) / 255) as u8; - data[i + 2] = ((b as u32) * (a as u32) / 255) as u8; + + if premultiply_alpha { + data[i + 0] = ((r as u32) * (a as u32) / 255) as u8; + data[i + 1] = ((g as u32) * (a as u32) / 255) as u8; + data[i + 2] = ((b as u32) * (a as u32) / 255) as u8; + } else { + data[i + 0] = r; + data[i + 1] = g; + data[i + 2] = b; + } } } @@ -58,6 +70,7 @@ pub fn load_from_memory(buffer: &[u8]) -> Option<Image> { height: image.height as u32, format: PixelFormat::RGBA8, bytes: IpcSharedMemory::from_bytes(&image.data[..]), + id: None, }) } stb_image2::LoadResult::ImageF32(_image) => { @@ -83,6 +96,7 @@ pub fn load_from_memory(buffer: &[u8]) -> Option<Image> { height: rgba.height(), format: PixelFormat::RGBA8, bytes: IpcSharedMemory::from_bytes(&*rgba), + id: None, }) } Err(e) => { diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index 3ab122d2bf7..0288efc206a 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -2,7 +2,7 @@ * 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 canvas_traits::{CanvasMsg, FromLayoutMsg}; +use canvas_traits::{CanvasMsg, FromLayoutMsg, CanvasData}; use dom::attr::Attr; use dom::attr::AttrValue; use dom::bindings::cell::DOMRefCell; @@ -202,10 +202,17 @@ impl HTMLCanvasElement { let data = if let Some(renderer) = self.ipc_renderer() { let (sender, receiver) = ipc::channel().unwrap(); - let msg = CanvasMsg::FromLayout(FromLayoutMsg::SendPixelContents(sender)); + let msg = CanvasMsg::FromLayout(FromLayoutMsg::SendData(sender)); renderer.send(msg).unwrap(); - receiver.recv().unwrap().to_vec() + match receiver.recv().unwrap() { + CanvasData::Pixels(pixel_data) + => pixel_data.image_data.to_vec(), + CanvasData::WebGL(_) + // TODO(ecoal95): Not sure if WebGL canvas is required for 2d spec, + // but I think it's not. + => return None, + } } else { repeat(0xffu8).take((size.height as usize) * (size.width as usize) * 4).collect() }; diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index ff2d6968e9a..aba9e32b45f 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -72,6 +72,7 @@ pub struct WebGLRenderingContext { #[ignore_heap_size_of = "Defined in ipc-channel"] ipc_renderer: IpcSender<CanvasMsg>, canvas: JS<HTMLCanvasElement>, + #[ignore_heap_size_of = "Defined in webrender_traits"] last_error: Cell<Option<WebGLError>>, texture_unpacking_settings: Cell<TextureUnpacking>, bound_texture_2d: MutNullableHeap<JS<WebGLTexture>>, @@ -462,6 +463,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { Some(data) => data, None => return self.webgl_error(InvalidValue), }; + if offset < 0 { return self.webgl_error(InvalidValue); } diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 39b651e0912..9c4a7af6185 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -15,7 +15,7 @@ dependencies = [ "gaol 0.0.1 (git+https://github.com/servo/gaol)", "gfx 0.0.1", "gfx_tests 0.0.1", - "gleam 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "glutin_app 0.0.1", "image 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.2.0 (git+https://github.com/servo/ipc-channel)", @@ -42,6 +42,8 @@ dependencies = [ "util 0.0.1", "util_tests 0.0.1", "webdriver_server 0.0.1", + "webrender 0.1.0 (git+https://github.com/glennw/webrender)", + "webrender_traits 0.1.0 (git+https://github.com/glennw/webrender_traits)", ] [[package]] @@ -152,7 +154,7 @@ dependencies = [ "canvas_traits 0.0.1", "euclid 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "gfx_traits 0.0.1", - "gleam 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.2.0 (git+https://github.com/servo/ipc-channel)", "layers 0.2.2 (git+https://github.com/servo/rust-layers)", "log 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -160,6 +162,7 @@ dependencies = [ "offscreen_gl_context 0.1.0 (git+https://github.com/ecoal95/rust-offscreen-rendering-context)", "plugins 0.0.1", "util 0.0.1", + "webrender_traits 0.1.0 (git+https://github.com/glennw/webrender_traits)", ] [[package]] @@ -179,6 +182,7 @@ dependencies = [ "serde 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde_macros 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "util 0.0.1", + "webrender_traits 0.1.0 (git+https://github.com/glennw/webrender_traits)", ] [[package]] @@ -200,7 +204,7 @@ name = "cgl" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gleam 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -273,7 +277,7 @@ dependencies = [ "gaol 0.0.1 (git+https://github.com/servo/gaol)", "gfx 0.0.1", "gfx_traits 0.0.1", - "gleam 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.2.0 (git+https://github.com/servo/ipc-channel)", "layers 0.2.2 (git+https://github.com/servo/rust-layers)", @@ -292,6 +296,8 @@ dependencies = [ "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "util 0.0.1", + "webrender 0.1.0 (git+https://github.com/glennw/webrender)", + "webrender_traits 0.1.0 (git+https://github.com/glennw/webrender_traits)", ] [[package]] @@ -680,6 +686,7 @@ dependencies = [ "unicode-script 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "util 0.0.1", + "webrender_traits 0.1.0 (git+https://github.com/glennw/webrender_traits)", ] [[package]] @@ -728,7 +735,7 @@ dependencies = [ [[package]] name = "gleam" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gl_generator 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -748,7 +755,7 @@ dependencies = [ "cgl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "compositing 0.0.1", "euclid 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "layers 0.2.2 (git+https://github.com/servo/rust-layers)", "msg 0.0.1", "net_traits 0.0.1", @@ -904,7 +911,7 @@ dependencies = [ "cgl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "leaky-cow 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -965,7 +972,7 @@ dependencies = [ "cgl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "glx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize_plugin 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1018,6 +1025,7 @@ dependencies = [ "unicode-script 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "util 0.0.1", + "webrender_traits 0.1.0 (git+https://github.com/glennw/webrender_traits)", ] [[package]] @@ -1034,6 +1042,7 @@ dependencies = [ "serde_macros 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "util 0.0.1", + "webrender_traits 0.1.0 (git+https://github.com/glennw/webrender_traits)", ] [[package]] @@ -1194,6 +1203,7 @@ dependencies = [ "serde_macros 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "util 0.0.1", + "webrender_traits 0.1.0 (git+https://github.com/glennw/webrender_traits)", ] [[package]] @@ -1218,6 +1228,7 @@ dependencies = [ "url 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "util 0.0.1", "uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "webrender_traits 0.1.0 (git+https://github.com/glennw/webrender_traits)", "websocket 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1334,7 +1345,7 @@ dependencies = [ "core-foundation 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "gl_generator 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "khronos_api 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1560,6 +1571,22 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "rustc_version" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scoped_threadpool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "script" version = "0.0.1" dependencies = [ @@ -1660,6 +1687,11 @@ dependencies = [ ] [[package]] +name = "semver" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "serde" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1766,7 +1798,7 @@ dependencies = [ "cgl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "expat-sys 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "glx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "io-surface 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2175,6 +2207,42 @@ dependencies = [ ] [[package]] +name = "webrender" +version = "0.1.0" +source = "git+https://github.com/glennw/webrender#90cd6afdd64cdb48f606dbe0ce5d1ac352329004" +dependencies = [ + "app_units 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-text 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "freetype 0.1.0 (git+https://github.com/servo/rust-freetype)", + "gleam 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ipc-channel 0.2.0 (git+https://github.com/servo/ipc-channel)", + "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "offscreen_gl_context 0.1.0 (git+https://github.com/ecoal95/rust-offscreen-rendering-context)", + "scoped_threadpool 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", + "webrender_traits 0.1.0 (git+https://github.com/glennw/webrender_traits)", +] + +[[package]] +name = "webrender_traits" +version = "0.1.0" +source = "git+https://github.com/glennw/webrender_traits#8ccc1608590d219d1df54e49f2f1e887995b4d60" +dependencies = [ + "app_units 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ipc-channel 0.2.0 (git+https://github.com/servo/ipc-channel)", + "offscreen_gl_context 0.1.0 (git+https://github.com/ecoal95/rust-offscreen-rendering-context)", + "serde 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_macros 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "websocket" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml index c4d2c6d9f36..9bea00cd956 100644 --- a/components/servo/Cargo.toml +++ b/components/servo/Cargo.toml @@ -52,6 +52,12 @@ name = "reftest" path = "../../tests/reftest.rs" harness = false +[dependencies.webrender_traits] +git = "https://github.com/glennw/webrender_traits" + +[dependencies.webrender] +git = "https://github.com/glennw/webrender" + [features] default = ["glutin_app", "window", "webdriver"] window = ["glutin_app/window"] diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 6544b69e002..67b6875da48 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -48,6 +48,9 @@ mod export { #[cfg(feature = "webdriver")] extern crate webdriver_server; +extern crate webrender; +extern crate webrender_traits; + #[cfg(feature = "webdriver")] fn webdriver(port: u16, constellation: Sender<ConstellationMsg>) { webdriver_server::start_server(port, constellation); @@ -82,6 +85,7 @@ use std::borrow::Borrow; use std::rc::Rc; use std::sync::mpsc::Sender; use util::opts; +use util::resource_files::resources_dir_path; pub use _util as util; pub use export::canvas; @@ -147,6 +151,35 @@ impl Browser { devtools::start_server(port) }); + let (webrender, webrender_api_sender) = if opts::get().use_webrender { + let mut resource_path = resources_dir_path(); + resource_path.push("shaders"); + + // TODO(gw): Duplicates device_pixels_per_screen_px from compositor. Tidy up! + let hidpi_factor = window.as_ref() + .map(|window| window.hidpi_factor().get()) + .unwrap_or(1.0); + let device_pixel_ratio = match opts.device_pixels_per_px { + Some(device_pixels_per_px) => device_pixels_per_px, + None => match opts.output_file { + Some(_) => 1.0, + None => hidpi_factor, + } + }; + + let (webrender, webrender_sender) = + webrender::Renderer::new(webrender::RendererOptions { + device_pixel_ratio: device_pixel_ratio, + resource_path: resource_path, + enable_aa: opts.enable_text_antialiasing, + enable_msaa: opts.use_msaa, + enable_profiler: opts.webrender_stats, + }); + (Some(webrender), Some(webrender_sender)) + } else { + (None, None) + }; + // Create the constellation, which maintains the engine // pipelines, including the script and layout threads, as well // as the navigation context. @@ -155,7 +188,8 @@ impl Browser { time_profiler_chan.clone(), mem_profiler_chan.clone(), devtools_chan, - supports_clipboard); + supports_clipboard, + webrender_api_sender.clone()); if cfg!(feature = "webdriver") { if let Some(port) = opts.webdriver_port { @@ -171,6 +205,8 @@ impl Browser { constellation_chan: constellation_chan, time_profiler_chan: time_profiler_chan, mem_profiler_chan: mem_profiler_chan, + webrender: webrender, + webrender_api_sender: webrender_api_sender, }); Browser { @@ -200,11 +236,13 @@ fn create_constellation(opts: opts::Opts, time_profiler_chan: time::ProfilerChan, mem_profiler_chan: mem::ProfilerChan, devtools_chan: Option<Sender<devtools_traits::DevtoolsControlMsg>>, - supports_clipboard: bool) -> Sender<ConstellationMsg> { + supports_clipboard: bool, + webrender_api_sender: Option<webrender_traits::RenderApiSender>) -> Sender<ConstellationMsg> { let resource_thread = new_resource_thread(opts.user_agent.clone(), devtools_chan.clone()); - - let image_cache_thread = new_image_cache_thread(resource_thread.clone()); - let font_cache_thread = FontCacheThread::new(resource_thread.clone()); + let image_cache_thread = new_image_cache_thread(resource_thread.clone(), + webrender_api_sender.as_ref().map(|wr| wr.create_api())); + let font_cache_thread = FontCacheThread::new(resource_thread.clone(), + webrender_api_sender.as_ref().map(|wr| wr.create_api())); let storage_thread: StorageThread = StorageThreadFactory::new(); let initial_state = InitialConstellationState { @@ -217,6 +255,7 @@ fn create_constellation(opts: opts::Opts, time_profiler_chan: time_profiler_chan, mem_profiler_chan: mem_profiler_chan, supports_clipboard: supports_clipboard, + webrender_api_sender: webrender_api_sender, }; let constellation_chan = Constellation::<layout::layout_thread::LayoutThread, diff --git a/components/util/opts.rs b/components/util/opts.rs index b154abe211f..d2dbdc1c382 100644 --- a/components/util/opts.rs +++ b/components/util/opts.rs @@ -183,6 +183,15 @@ pub struct Opts { /// Enable vsync in the compositor pub enable_vsync: bool, + + /// True to enable the webrender painting/compositing backend. + pub use_webrender: bool, + + /// True to show webrender profiling stats on screen. + pub webrender_stats: bool, + + /// True if WebRender should use multisample antialiasing. + pub use_msaa: bool, } fn print_usage(app: &str, opts: &Options) { @@ -272,6 +281,12 @@ pub struct DebugOptions { /// Disable vsync in the compositor pub disable_vsync: bool, + + /// Show webrender profiling stats on screen. + pub webrender_stats: bool, + + /// Use multisample antialiasing in WebRender. + pub use_msaa: bool, } @@ -307,6 +322,8 @@ impl DebugOptions { "gc-profile" => debug_options.gc_profile = true, "load-webfonts-synchronously" => debug_options.load_webfonts_synchronously = true, "disable-vsync" => debug_options.disable_vsync = true, + "wr-stats" => debug_options.webrender_stats = true, + "msaa" => debug_options.use_msaa = true, "" => {}, _ => return Err(option) }; @@ -354,6 +371,8 @@ pub fn print_debug_usage(app: &str) -> ! { "Load web fonts synchronously to avoid non-deterministic network-driven reflows"); print_option("disable-vsync", "Disable vsync mode in the compositor to allow profiling at more than monitor refresh rate"); + print_option("wr-stats", "Show WebRender profiler on screen."); + print_option("msaa", "Use multisample antialiasing in WebRender."); println!(""); @@ -483,6 +502,9 @@ pub fn default_opts() -> Opts { exit_after_load: false, no_native_titlebar: false, enable_vsync: true, + use_webrender: false, + webrender_stats: false, + use_msaa: false, } } @@ -526,6 +548,7 @@ pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult { opts.optmulti("", "pref", "A preference to set to enable", "dom.mozbrowser.enabled"); opts.optflag("b", "no-native-titlebar", "Do not use native titlebar"); + opts.optflag("w", "webrender", "Use webrender backend"); let opt_match = match opts.parse(args) { Ok(m) => m, @@ -668,6 +691,8 @@ pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult { (contents, url) }).collect(); + let use_webrender = opt_match.opt_present("w") && !opt_match.opt_present("z"); + let opts = Opts { is_running_problem_test: is_running_problem_test, url: Some(url), @@ -717,6 +742,9 @@ pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult { exit_after_load: opt_match.opt_present("x"), no_native_titlebar: opt_match.opt_present("b"), enable_vsync: !debug_options.disable_vsync, + use_webrender: use_webrender, + webrender_stats: debug_options.webrender_stats, + use_msaa: debug_options.use_msaa, }; set_defaults(opts); |