diff options
author | bors-servo <metajack+bors@gmail.com> | 2015-04-20 19:29:02 -0500 |
---|---|---|
committer | bors-servo <metajack+bors@gmail.com> | 2015-04-20 19:29:02 -0500 |
commit | e4b620ea54c94e03095e4108bce94ec750416bba (patch) | |
tree | 0af4f8dcc0b8d4e73c431ae66c5d90d62cecbfca /components/canvas | |
parent | 99c6fbc3a439eb9fb43a2b0c2baeb14e54e50800 (diff) | |
parent | c82485874d1fe1db89a2dac61c6d612a75b8e1a1 (diff) | |
download | servo-e4b620ea54c94e03095e4108bce94ec750416bba.tar.gz servo-e4b620ea54c94e03095e4108bce94ec750416bba.zip |
Auto merge of #5652 - dmarcos:webgl, r=jdm
@jdm @ecoal95 I'm working on making VR happen in the Browser and I want to bring to Servo the [webVR APIs](https://github.com/MozVR/webvr-spec/blob/master/webvr.idl) we already have in Gecko. Before anything happens we need a working implementation of WebGL (and also the [fullscreen API](https://fullscreen.spec.whatwg.org/)). My implementation is very basic and probably naive (I just recently started to contribute to Servo). My patch is just a starting point:
- It only implements ```clearColor``` and ```clear``` methods of the [WebGL spec](https://www.khronos.org/registry/webgl/specs/latest/).
- It uses the readback strategy that ```canvasRenderingContext2D``` is using (The webgl task paints stuff independently on it's own buffer and the compositor task request the pixels back to the webgl task when it needs them) I'm sure there are much better ways to handle this. Latency and FPS are critical in VR so we have to figure out the fastest way to push pixels to the screen. I've read something about layerizing the canvas but I'm still not sure what that even means :)
- There's an included test you can try ```./mach run tests/ref/webgl-context/clearcolor.html```
@ecoal95 I know you'll be working on this for the next three months. With a foundation in place we will be able to make quick progress in parallel. This is exciting!
<!-- Reviewable:start -->
[<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/5652)
<!-- Reviewable:end -->
Diffstat (limited to 'components/canvas')
-rw-r--r-- | components/canvas/Cargo.toml | 11 | ||||
-rw-r--r-- | components/canvas/canvas_msg.rs | 61 | ||||
-rw-r--r-- | components/canvas/canvas_paint_task.rs | 131 | ||||
-rw-r--r-- | components/canvas/lib.rs | 6 | ||||
-rw-r--r-- | components/canvas/webgl_paint_task.rs | 112 |
5 files changed, 243 insertions, 78 deletions
diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index a682f1069bb..df07dbd1de0 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -16,6 +16,17 @@ git = "https://github.com/servo/rust-cssparser" [dependencies.geom] git = "https://github.com/servo/rust-geom" +[dependencies.gleam] +git = "https://github.com/servo/gleam" + +[dependencies.glutin] +git = "https://github.com/servo/glutin" +branch = "servo" +features = ["headless"] + +[dependencies.msg] +path = "../msg" + [dependencies.util] path = "../util" diff --git a/components/canvas/canvas_msg.rs b/components/canvas/canvas_msg.rs new file mode 100644 index 00000000000..3d21da3ae0a --- /dev/null +++ b/components/canvas/canvas_msg.rs @@ -0,0 +1,61 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use canvas_paint_task::{FillOrStrokeStyle, LineCapStyle, LineJoinStyle}; +use geom::matrix2d::Matrix2D; +use geom::point::Point2D; +use geom::rect::Rect; +use geom::size::Size2D; +use std::sync::mpsc::{Sender}; + +#[derive(Clone)] +pub enum CanvasMsg { + Canvas2d(Canvas2dMsg), + Common(CanvasCommonMsg), + WebGL(CanvasWebGLMsg), +} + +#[derive(Clone)] +pub enum Canvas2dMsg { + Arc(Point2D<f32>, f32, f32, f32, bool), + ArcTo(Point2D<f32>, Point2D<f32>, f32), + DrawImage(Vec<u8>, Size2D<f64>, Rect<f64>, Rect<f64>, bool), + DrawImageSelf(Size2D<f64>, Rect<f64>, Rect<f64>, bool), + BeginPath, + BezierCurveTo(Point2D<f32>, Point2D<f32>, Point2D<f32>), + ClosePath, + ClearRect(Rect<f32>), + Fill, + FillRect(Rect<f32>), + GetImageData(Rect<f64>, Size2D<f64>, Sender<Vec<u8>>), + LineTo(Point2D<f32>), + MoveTo(Point2D<f32>), + PutImageData(Vec<u8>, Rect<f64>, Option<Rect<f64>>), + QuadraticCurveTo(Point2D<f32>, Point2D<f32>), + RestoreContext, + SaveContext, + StrokeRect(Rect<f32>), + Stroke, + SetFillStyle(FillOrStrokeStyle), + SetStrokeStyle(FillOrStrokeStyle), + SetLineWidth(f32), + SetLineCap(LineCapStyle), + SetLineJoin(LineJoinStyle), + SetMiterLimit(f32), + SetGlobalAlpha(f32), + SetTransform(Matrix2D<f32>), +} + +#[derive(Clone)] +pub enum CanvasWebGLMsg { + Clear(u32), + ClearColor(f32, f32, f32, f32), +} + +#[derive(Clone)] +pub enum CanvasCommonMsg { + Close, + Recreate(Size2D<i32>), + SendPixelContents(Sender<Vec<u8>>), +} diff --git a/components/canvas/canvas_paint_task.rs b/components/canvas/canvas_paint_task.rs index f45c4d79c14..bbce09cea23 100644 --- a/components/canvas/canvas_paint_task.rs +++ b/components/canvas/canvas_paint_task.rs @@ -6,6 +6,7 @@ use azure::azure::AzFloat; use azure::azure_hl::{DrawTarget, SurfaceFormat, BackendType, StrokeOptions, DrawOptions, Pattern}; use azure::azure_hl::{ColorPattern, PathBuilder, JoinStyle, CapStyle, DrawSurfaceOptions, Filter}; use azure::azure_hl::{GradientStop, LinearGradientPattern, RadialGradientPattern, ExtendMode}; +use canvas_msg::{CanvasMsg, Canvas2dMsg, CanvasCommonMsg}; use geom::matrix2d::Matrix2D; use geom::point::Point2D; use geom::rect::Rect; @@ -20,40 +21,6 @@ use std::mem; use std::num::{Float, ToPrimitive}; use std::sync::mpsc::{channel, Sender}; -#[derive(Clone)] -pub enum CanvasMsg { - SaveContext, - RestoreContext, - FillRect(Rect<f32>), - ClearRect(Rect<f32>), - StrokeRect(Rect<f32>), - BeginPath, - ClosePath, - Fill, - Stroke, - DrawImage(Vec<u8>, Size2D<f64>, Rect<f64>, Rect<f64>, bool), - DrawImageSelf(Size2D<f64>, Rect<f64>, Rect<f64>, bool), - MoveTo(Point2D<f32>), - LineTo(Point2D<f32>), - QuadraticCurveTo(Point2D<f32>, Point2D<f32>), - BezierCurveTo(Point2D<f32>, Point2D<f32>, Point2D<f32>), - Arc(Point2D<f32>, f32, f32, f32, bool), - ArcTo(Point2D<f32>, Point2D<f32>, f32), - SetFillStyle(FillOrStrokeStyle), - SetStrokeStyle(FillOrStrokeStyle), - SetLineWidth(f32), - SetLineCap(LineCapStyle), - SetLineJoin(LineJoinStyle), - SetMiterLimit(f32), - SetTransform(Matrix2D<f32>), - SetGlobalAlpha(f32), - Recreate(Size2D<i32>), - SendPixelContents(Sender<Vec<u8>>), - GetImageData(Rect<f64>, Size2D<f64>, Sender<Vec<u8>>), - PutImageData(Vec<u8>, Rect<f64>, Option<Rect<f64>>), - Close, -} - impl<'a> CanvasPaintTask<'a> { /// It reads image data from the canvas /// canvas_size: The size of the canvas we're reading from @@ -236,49 +203,59 @@ impl<'a> CanvasPaintTask<'a> { loop { match port.recv().unwrap() { - CanvasMsg::SaveContext => painter.save_context_state(), - CanvasMsg::RestoreContext => painter.restore_context_state(), - CanvasMsg::FillRect(ref rect) => painter.fill_rect(rect), - CanvasMsg::StrokeRect(ref rect) => painter.stroke_rect(rect), - CanvasMsg::ClearRect(ref rect) => painter.clear_rect(rect), - CanvasMsg::BeginPath => painter.begin_path(), - CanvasMsg::ClosePath => painter.close_path(), - CanvasMsg::Fill => painter.fill(), - CanvasMsg::Stroke => painter.stroke(), - CanvasMsg::DrawImage(imagedata, image_size, dest_rect, source_rect, smoothing_enabled) => { - painter.draw_image(imagedata, image_size, dest_rect, source_rect, smoothing_enabled) - } - CanvasMsg::DrawImageSelf(image_size, dest_rect, source_rect, smoothing_enabled) => { - painter.draw_image_self(image_size, dest_rect, source_rect, smoothing_enabled) - } - CanvasMsg::MoveTo(ref point) => painter.move_to(point), - CanvasMsg::LineTo(ref point) => painter.line_to(point), - CanvasMsg::QuadraticCurveTo(ref cp, ref pt) => { - painter.quadratic_curve_to(cp, pt) - } - CanvasMsg::BezierCurveTo(ref cp1, ref cp2, ref pt) => { - painter.bezier_curve_to(cp1, cp2, pt) - } - CanvasMsg::Arc(ref center, radius, start, end, ccw) => { - painter.arc(center, radius, start, end, ccw) - } - CanvasMsg::ArcTo(ref cp1, ref cp2, radius) => { - painter.arc_to(cp1, cp2, radius) - } - CanvasMsg::SetFillStyle(style) => painter.set_fill_style(style), - CanvasMsg::SetStrokeStyle(style) => painter.set_stroke_style(style), - CanvasMsg::SetLineWidth(width) => painter.set_line_width(width), - CanvasMsg::SetLineCap(cap) => painter.set_line_cap(cap), - CanvasMsg::SetLineJoin(join) => painter.set_line_join(join), - CanvasMsg::SetMiterLimit(limit) => painter.set_miter_limit(limit), - CanvasMsg::SetTransform(ref matrix) => painter.set_transform(matrix), - CanvasMsg::SetGlobalAlpha(alpha) => painter.set_global_alpha(alpha), - CanvasMsg::Recreate(size) => painter.recreate(size), - CanvasMsg::SendPixelContents(chan) => painter.send_pixel_contents(chan), - CanvasMsg::GetImageData(dest_rect, canvas_size, chan) => painter.get_image_data(dest_rect, canvas_size, chan), - CanvasMsg::PutImageData(imagedata, image_data_rect, dirty_rect) - => painter.put_image_data(imagedata, image_data_rect, dirty_rect), - CanvasMsg::Close => break, + CanvasMsg::Canvas2d(message) => { + match message { + Canvas2dMsg::FillRect(ref rect) => painter.fill_rect(rect), + Canvas2dMsg::StrokeRect(ref rect) => painter.stroke_rect(rect), + Canvas2dMsg::ClearRect(ref rect) => painter.clear_rect(rect), + Canvas2dMsg::BeginPath => painter.begin_path(), + Canvas2dMsg::ClosePath => painter.close_path(), + Canvas2dMsg::Fill => painter.fill(), + Canvas2dMsg::Stroke => painter.stroke(), + Canvas2dMsg::DrawImage(imagedata, image_size, dest_rect, source_rect, smoothing_enabled) => { + painter.draw_image(imagedata, image_size, dest_rect, source_rect, smoothing_enabled) + } + Canvas2dMsg::DrawImageSelf(image_size, dest_rect, source_rect, smoothing_enabled) => { + painter.draw_image_self(image_size, dest_rect, source_rect, smoothing_enabled) + } + Canvas2dMsg::MoveTo(ref point) => painter.move_to(point), + Canvas2dMsg::LineTo(ref point) => painter.line_to(point), + Canvas2dMsg::QuadraticCurveTo(ref cp, ref pt) => { + painter.quadratic_curve_to(cp, pt) + } + Canvas2dMsg::BezierCurveTo(ref cp1, ref cp2, ref pt) => { + painter.bezier_curve_to(cp1, cp2, pt) + } + Canvas2dMsg::Arc(ref center, radius, start, end, ccw) => { + painter.arc(center, radius, start, end, ccw) + } + Canvas2dMsg::ArcTo(ref cp1, ref cp2, radius) => { + painter.arc_to(cp1, cp2, radius) + } + Canvas2dMsg::RestoreContext => painter.restore_context_state(), + Canvas2dMsg::SaveContext => painter.save_context_state(), + Canvas2dMsg::SetFillStyle(style) => painter.set_fill_style(style), + Canvas2dMsg::SetStrokeStyle(style) => painter.set_stroke_style(style), + Canvas2dMsg::SetLineWidth(width) => painter.set_line_width(width), + Canvas2dMsg::SetLineCap(cap) => painter.set_line_cap(cap), + Canvas2dMsg::SetLineJoin(join) => painter.set_line_join(join), + Canvas2dMsg::SetMiterLimit(limit) => painter.set_miter_limit(limit), + Canvas2dMsg::SetTransform(ref matrix) => painter.set_transform(matrix), + Canvas2dMsg::SetGlobalAlpha(alpha) => painter.set_global_alpha(alpha), + Canvas2dMsg::GetImageData(dest_rect, canvas_size, chan) => painter.get_image_data(dest_rect, canvas_size, chan), + Canvas2dMsg::PutImageData(imagedata, image_data_rect, dirty_rect) + => painter.put_image_data(imagedata, image_data_rect, dirty_rect), + } + }, + CanvasMsg::Common(message) => { + match message { + CanvasCommonMsg::Close => break, + CanvasCommonMsg::Recreate(size) => painter.recreate(size), + CanvasCommonMsg::SendPixelContents(chan) => + painter.send_pixel_contents(chan), + } + }, + CanvasMsg::WebGL(_) => panic!("Wrong message sent to Canvas2D task"), } } }); diff --git a/components/canvas/lib.rs b/components/canvas/lib.rs index 2b5c51feec1..5feab435394 100644 --- a/components/canvas/lib.rs +++ b/components/canvas/lib.rs @@ -11,6 +11,10 @@ extern crate cssparser; extern crate geom; extern crate gfx; extern crate util; - +extern crate gleam; +extern crate msg; +extern crate glutin; pub mod canvas_paint_task; +pub mod webgl_paint_task; +pub mod canvas_msg; diff --git a/components/canvas/webgl_paint_task.rs b/components/canvas/webgl_paint_task.rs new file mode 100644 index 00000000000..1afcf20089c --- /dev/null +++ b/components/canvas/webgl_paint_task.rs @@ -0,0 +1,112 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use canvas_msg::{CanvasWebGLMsg, CanvasCommonMsg, CanvasMsg}; +use geom::size::Size2D; + +use gleam::gl; +use gleam::gl::types::{GLint, GLsizei}; + +use util::task::spawn_named; + +use std::borrow::ToOwned; +use std::sync::mpsc::{channel, Sender}; +use util::vec::byte_swap; + +use glutin::{HeadlessRendererBuilder}; + +pub struct WebGLPaintTask { + size: Size2D<i32>, +} + +impl WebGLPaintTask { + fn new(size: Size2D<i32>) -> WebGLPaintTask { + WebGLPaintTask::create(size); + WebGLPaintTask { + size: size, + } + } + + pub fn start(size: Size2D<i32>) -> Sender<CanvasMsg> { + let (chan, port) = channel::<CanvasMsg>(); + spawn_named("WebGLTask".to_owned(), move || { + let mut painter = WebGLPaintTask::new(size); + painter.init(); + loop { + match port.recv().unwrap() { + CanvasMsg::WebGL(message) => { + match message { + CanvasWebGLMsg::Clear(mask) => painter.clear(mask), + CanvasWebGLMsg::ClearColor(r, g, b, a) => painter.clear_color(r, g, b, a), + } + }, + CanvasMsg::Common(message) => { + match message { + CanvasCommonMsg::Close => break, + CanvasCommonMsg::SendPixelContents(chan) => painter.send_pixel_contents(chan), + CanvasCommonMsg::Recreate(size) => painter.recreate(size), + } + }, + CanvasMsg::Canvas2d(_) => panic!("Wrong message sent to WebGLTask"), + } + } + }); + chan + } + + fn create(size: Size2D<i32>) { + // It creates OpenGL context + let context = HeadlessRendererBuilder::new(size.width as u32, size.height as u32).build().unwrap(); + unsafe { + context.make_current(); + } + } + + fn init(&self) { + let framebuffer_ids = gl::gen_framebuffers(1); + gl::bind_framebuffer(gl::FRAMEBUFFER, framebuffer_ids[0]); + + let texture_ids = gl::gen_textures(1); + gl::bind_texture(gl::TEXTURE_2D, texture_ids[0]); + + gl::tex_image_2d(gl::TEXTURE_2D, 0, gl::RGB as GLint, self.size.width as GLsizei, + self.size.height as GLsizei, 0, gl::RGB, gl::UNSIGNED_BYTE, None); + gl::tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint); + gl::tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint); + + gl::framebuffer_texture_2d(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, + texture_ids[0], 0); + gl::bind_texture(gl::TEXTURE_2D, 0); + + gl::viewport(0 as GLint, 0 as GLint, + self.size.width as GLsizei, self.size.height as GLsizei); + } + + fn clear(&self, mask: u32) { + gl::clear(mask); + } + + fn clear_color(&self, r: f32, g: f32, b: f32, a: f32) { + gl::clear_color(r, g, b, a); + } + + fn send_pixel_contents(&mut self, chan: Sender<Vec<u8>>) { + // FIXME(#5652, dmarcos) Instead of a readback strategy we have + // to layerize the canvas + 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); + + // rgba -> bgra + byte_swap(pixels.as_mut_slice()); + chan.send(pixels).unwrap(); + } + + fn recreate(&mut self, size: Size2D<i32>) { + self.size = size; + self.init(); + } + +} |