servo: Merge #5863 - Refactor WebGL implementation (reopens #5769) (from emilio:webgl-refactor); r=jdm

GitHub doesn't allow me to reopen #5769, so I created this.

Sorry about the merge fail, my bad :/

cc/ @jdm @dmarcos

---

This PR uses customized GL context creation code, right now only working under Linux, so I expect the clearcolor test to fail on other platforms.

It addresses some other problems:

* Propagates context creation error to the top, returning null if not found.
* Uses GLContextAttributes, which will allow us to write WebGLContextAttributes easily.
* Doesn't allow a 2d context and a WebGL context coexist.
* Panics when resizing the context to larger dimensions (to be fixed soon, but better than blindly allowing it).
    Removes some unused dependencies

Source-Repo: https://github.com/servo/servo
Source-Revision: 9f2ad9376eaf598898387ea2c26f48c3ceb0330d
This commit is contained in:
ecoal95 2015-04-26 21:28:35 -05:00
parent 32cc0bb8fb
commit 086acefea5
9 changed files with 171 additions and 93 deletions

View File

@ -19,16 +19,16 @@ 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"
[dependencies.gfx]
path = "../gfx"
[dependencies.glutin]
git = "https://github.com/servo/glutin"
branch = "servo"
features = ["headless"]
[dependencies.offscreen_gl_context]
git = "https://github.com/ecoal95/rust-offscreen-rendering-context"

View File

@ -5,6 +5,7 @@
#![feature(collections)]
#![feature(core)]
#![feature(std_misc)]
#![feature(rustc_private)]
extern crate azure;
extern crate cssparser;
@ -12,9 +13,12 @@ extern crate geom;
extern crate gfx;
extern crate util;
extern crate gleam;
extern crate msg;
extern crate offscreen_gl_context;
extern crate glutin;
#[macro_use]
extern crate log;
pub mod canvas_paint_task;
pub mod webgl_paint_task;
pub mod canvas_msg;

View File

@ -2,36 +2,73 @@
* 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 canvas_msg::{CanvasMsg, CanvasWebGLMsg, CanvasCommonMsg};
use geom::size::Size2D;
use gleam::gl;
use gleam::gl::types::{GLint, GLsizei};
use gleam::gl::types::{GLsizei};
use util::task::spawn_named;
use std::borrow::ToOwned;
use std::sync::mpsc::{channel, Sender};
use util::vec::byte_swap;
use offscreen_gl_context::{GLContext, GLContextAttributes};
use glutin::{HeadlessRendererBuilder};
use glutin::{HeadlessRendererBuilder, HeadlessContext};
// FIXME(ecoal95): We use glutin as a fallback until GLContext support improves.
enum PlatformIndependentContext {
GLContext(GLContext),
GlutinContext(HeadlessContext),
}
impl PlatformIndependentContext {
fn make_current(&self) {
match *self {
PlatformIndependentContext::GLContext(ref ctx) => ctx.make_current().unwrap(),
PlatformIndependentContext::GlutinContext(ref ctx) => unsafe { ctx.make_current() }
}
}
}
fn create_offscreen_context(size: Size2D<i32>, attrs: GLContextAttributes) -> Result<PlatformIndependentContext, &'static str> {
match GLContext::create_offscreen(size, attrs) {
Ok(ctx) => Ok(PlatformIndependentContext::GLContext(ctx)),
Err(msg) => {
debug!("GLContext creation error: {}", msg);
match HeadlessRendererBuilder::new(size.width as u32, size.height as u32).build() {
Ok(ctx) => Ok(PlatformIndependentContext::GlutinContext(ctx)),
Err(_) => Err("Glutin headless context creation failed")
}
}
}
}
pub struct WebGLPaintTask {
size: Size2D<i32>,
original_context_size: Size2D<i32>,
gl_context: PlatformIndependentContext,
}
// This allows trying to create the PaintTask
// before creating the thread
unsafe impl Send for WebGLPaintTask {}
impl WebGLPaintTask {
fn new(size: Size2D<i32>) -> WebGLPaintTask {
WebGLPaintTask::create(size);
WebGLPaintTask {
fn new(size: Size2D<i32>) -> Result<WebGLPaintTask, &'static str> {
let context = try!(create_offscreen_context(size, GLContextAttributes::default()));
Ok(WebGLPaintTask {
size: size,
}
original_context_size: size,
gl_context: context
})
}
pub fn start(size: Size2D<i32>) -> Sender<CanvasMsg> {
pub fn start(size: Size2D<i32>) -> Result<Sender<CanvasMsg>, &'static str> {
let (chan, port) = channel::<CanvasMsg>();
let mut painter = try!(WebGLPaintTask::new(size));
spawn_named("WebGLTask".to_owned(), move || {
let mut painter = WebGLPaintTask::new(size);
painter.init();
loop {
match port.recv().unwrap() {
@ -52,35 +89,8 @@ impl WebGLPaintTask {
}
}
});
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);
Ok(chan)
}
fn clear(&self, mask: u32) {
@ -95,9 +105,9 @@ impl WebGLPaintTask {
// 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);
self.size.width as gl::GLsizei,
self.size.height as gl::GLsizei,
gl::RGBA, gl::UNSIGNED_BYTE);
// rgba -> bgra
byte_swap(&mut pixels);
@ -105,8 +115,19 @@ impl WebGLPaintTask {
}
fn recreate(&mut self, size: Size2D<i32>) {
self.size = size;
self.init();
// TODO(ecoal95): GLContext should support a resize() method
if size.width > self.original_context_size.width ||
size.height > self.original_context_size.height {
panic!("Can't grow a GLContext (yet)");
} else {
// Right now we just crop the viewport, it will do the job
self.size = size;
gl::viewport(0, 0, size.width, size.height);
unsafe { gl::Scissor(0, 0, size.width, size.height); }
}
}
fn init(&mut self) {
self.gl_context.make_current();
}
}

View File

@ -169,24 +169,37 @@ impl<'a> HTMLCanvasElementMethods for JSRef<'a, HTMLCanvasElement> {
fn GetContext(self, id: DOMString) -> Option<CanvasRenderingContext2DOrWebGLRenderingContext> {
match &*id {
"2d" => {
let context_2d = self.context_2d.or_init(|| {
let window = window_from_node(self).root();
let size = self.get_size();
CanvasRenderingContext2D::new(GlobalRef::Window(window.r()), self, size)
});
Some(CanvasRenderingContext2DOrWebGLRenderingContext::eCanvasRenderingContext2D(Unrooted::from_temporary(context_2d)))
}
"webgl" | "experimental-webgl" => {
let context_webgl = self.context_webgl.or_init(|| {
let window = window_from_node(self).root();
let size = self.get_size();
WebGLRenderingContext::new(GlobalRef::Window(window.r()), self, size)
});
Some(CanvasRenderingContext2DOrWebGLRenderingContext::eWebGLRenderingContext(Unrooted::from_temporary(context_webgl)))
}
_ => return None
}
"2d" => {
if self.context_webgl.get().is_some() {
debug!("Trying to get a 2d context for a canvas with an already initialized WebGL context");
return None;
}
let context_2d = self.context_2d.or_init(|| {
let window = window_from_node(self).root();
let size = self.get_size();
CanvasRenderingContext2D::new(GlobalRef::Window(window.r()), self, size)
});
Some(CanvasRenderingContext2DOrWebGLRenderingContext::eCanvasRenderingContext2D(Unrooted::from_temporary(context_2d)))
}
"webgl" | "experimental-webgl" => {
if self.context_2d.get().is_some() {
debug!("Trying to get a WebGL context for a canvas with an already initialized 2d context");
return None;
}
if !self.context_webgl.get().is_some() {
let window = window_from_node(self).root();
let size = self.get_size();
self.context_webgl.assign(WebGLRenderingContext::new(GlobalRef::Window(window.r()), self, size))
}
self.context_webgl.get().map( |ctx|
CanvasRenderingContext2DOrWebGLRenderingContext::eWebGLRenderingContext(Unrooted::from_temporary(ctx)))
}
_ => None
}
}
}
@ -226,11 +239,11 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLCanvasElement> {
let value = attr.value();
let recreate = match attr.local_name() {
&atom!("width") => {
self.width.set(parse_unsigned_integer(value.chars()).unwrap_or(DEFAULT_WIDTH));
self.width.set(parse_unsigned_integer(value.as_slice().chars()).unwrap_or(DEFAULT_WIDTH));
true
}
&atom!("height") => {
self.height.set(parse_unsigned_integer(value.chars()).unwrap_or(DEFAULT_HEIGHT));
self.height.set(parse_unsigned_integer(value.as_slice().chars()).unwrap_or(DEFAULT_HEIGHT));
true
}
_ => false,

View File

@ -23,25 +23,32 @@ pub struct WebGLRenderingContext {
impl WebGLRenderingContext {
fn new_inherited(global: GlobalRef, canvas: JSRef<HTMLCanvasElement>, size: Size2D<i32>)
-> WebGLRenderingContext {
WebGLRenderingContext {
-> Result<WebGLRenderingContext, &'static str> {
let chan = try!(WebGLPaintTask::start(size));
Ok(WebGLRenderingContext {
reflector_: Reflector::new(),
global: GlobalField::from_rooted(&global),
renderer: WebGLPaintTask::start(size),
renderer: chan,
canvas: JS::from_rooted(canvas),
}
})
}
pub fn new(global: GlobalRef, canvas: JSRef<HTMLCanvasElement>, size: Size2D<i32>)
-> Temporary<WebGLRenderingContext> {
reflect_dom_object(box WebGLRenderingContext::new_inherited(global, canvas, size),
global, WebGLRenderingContextBinding::Wrap)
-> Option<Temporary<WebGLRenderingContext>> {
match WebGLRenderingContext::new_inherited(global, canvas, size) {
Ok(ctx) => Some(reflect_dom_object(box ctx, global,
WebGLRenderingContextBinding::Wrap)),
Err(msg) => {
error!("Couldn't create WebGLRenderingContext: {}", msg);
None
}
}
}
pub fn recreate(&self, size: Size2D<i32>) {
self.renderer.send(CanvasMsg::Common(CanvasCommonMsg::Recreate(size))).unwrap();
}
}
#[unsafe_destructor]
@ -72,4 +79,3 @@ impl LayoutCanvasWebGLRenderingContextHelpers for LayoutJS<WebGLRenderingContext
(*self.unsafe_get()).renderer.clone()
}
}

View File

@ -3,7 +3,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/. */
// https://www.whatwg.org/html/#htmlcanvaselement
// https://www.whatwg.org/html/#htmlcanvaselement
typedef (CanvasRenderingContext2D or WebGLRenderingContext) RenderingContext;
interface HTMLCanvasElement : HTMLElement {

View File

@ -34,7 +34,7 @@ version = "0.0.1"
[[package]]
name = "android_glue"
version = "0.0.1"
source = "git+https://github.com/tomaka/android-rs-glue#5a68056599fb498b0cf3715fd359894825e3b856"
source = "git+https://github.com/tomaka/android-rs-glue#fa1eaa1d1857e0fa93832c5c8db5dc8bbcbc1eba"
[[package]]
name = "azure"
@ -67,7 +67,7 @@ dependencies = [
"gfx 0.0.1",
"gleam 0.0.1 (git+https://github.com/servo/gleam)",
"glutin 0.0.7 (git+https://github.com/servo/glutin?branch=servo)",
"msg 0.0.1",
"offscreen_gl_context 0.0.1 (git+https://github.com/ecoal95/rust-offscreen-rendering-context)",
"util 0.0.1",
]
@ -318,7 +318,7 @@ name = "gdi32-sys"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -517,7 +517,7 @@ name = "kernel32-sys"
version = "0.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -709,6 +709,19 @@ dependencies = [
"util 0.0.1",
]
[[package]]
name = "offscreen_gl_context"
version = "0.0.1"
source = "git+https://github.com/ecoal95/rust-offscreen-rendering-context#9547d416019b15e0e59dc5176f5b1a14e14b44ca"
dependencies = [
"cgl 0.0.1 (git+https://github.com/servo/rust-cgl)",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"gleam 0.0.1 (git+https://github.com/servo/gleam)",
"glx 0.0.1 (git+https://github.com/servo/rust-glx)",
"libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"xlib 0.1.0 (git+https://github.com/servo/rust-xlib)",
]
[[package]]
name = "openssl"
version = "0.5.1"
@ -1000,7 +1013,7 @@ name = "user32-sys"
version = "0.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1082,11 +1095,6 @@ dependencies = [
"rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi"
version = "0.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.1.15"

View File

@ -65,7 +65,7 @@ dependencies = [
"gfx 0.0.1",
"gleam 0.0.1 (git+https://github.com/servo/gleam)",
"glutin 0.0.7 (git+https://github.com/servo/glutin?branch=servo)",
"msg 0.0.1",
"offscreen_gl_context 0.0.1 (git+https://github.com/ecoal95/rust-offscreen-rendering-context)",
"util 0.0.1",
]
@ -687,6 +687,19 @@ dependencies = [
"util 0.0.1",
]
[[package]]
name = "offscreen_gl_context"
version = "0.0.1"
source = "git+https://github.com/ecoal95/rust-offscreen-rendering-context#9547d416019b15e0e59dc5176f5b1a14e14b44ca"
dependencies = [
"cgl 0.0.1 (git+https://github.com/servo/rust-cgl)",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"gleam 0.0.1 (git+https://github.com/servo/gleam)",
"glx 0.0.1 (git+https://github.com/servo/rust-glx)",
"libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"xlib 0.1.0 (git+https://github.com/servo/rust-xlib)",
]
[[package]]
name = "openssl"
version = "0.5.1"

View File

@ -59,7 +59,7 @@ dependencies = [
"gfx 0.0.1",
"gleam 0.0.1 (git+https://github.com/servo/gleam)",
"glutin 0.0.7 (git+https://github.com/servo/glutin?branch=servo)",
"msg 0.0.1",
"offscreen_gl_context 0.0.1 (git+https://github.com/ecoal95/rust-offscreen-rendering-context)",
"util 0.0.1",
]
@ -679,6 +679,19 @@ dependencies = [
"malloc_buf 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "offscreen_gl_context"
version = "0.0.1"
source = "git+https://github.com/ecoal95/rust-offscreen-rendering-context#9547d416019b15e0e59dc5176f5b1a14e14b44ca"
dependencies = [
"cgl 0.0.1 (git+https://github.com/servo/rust-cgl)",
"geom 0.1.0 (git+https://github.com/servo/rust-geom)",
"gleam 0.0.1 (git+https://github.com/servo/gleam)",
"glx 0.0.1 (git+https://github.com/servo/rust-glx)",
"libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"xlib 0.1.0 (git+https://github.com/servo/rust-xlib)",
]
[[package]]
name = "openssl"
version = "0.5.1"