WebGL context hardware acceleration + error detection

This commit is contained in:
ecoal95 2015-04-21 09:16:48 +02:00
parent ea00e949a4
commit 79a5dae170
10 changed files with 172 additions and 94 deletions

View file

@ -19,16 +19,16 @@ git = "https://github.com/servo/rust-geom"
[dependencies.gleam] [dependencies.gleam]
git = "https://github.com/servo/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] [dependencies.util]
path = "../util" path = "../util"
[dependencies.gfx] [dependencies.gfx]
path = "../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(collections)]
#![feature(core)] #![feature(core)]
#![feature(std_misc)] #![feature(std_misc)]
#![feature(rustc_private)]
extern crate azure; extern crate azure;
extern crate cssparser; extern crate cssparser;
@ -12,9 +13,12 @@ extern crate geom;
extern crate gfx; extern crate gfx;
extern crate util; extern crate util;
extern crate gleam; extern crate gleam;
extern crate msg; extern crate offscreen_gl_context;
extern crate glutin; extern crate glutin;
#[macro_use]
extern crate log;
pub mod canvas_paint_task; pub mod canvas_paint_task;
pub mod webgl_paint_task; pub mod webgl_paint_task;
pub mod canvas_msg; 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 * 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/. */ * 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 geom::size::Size2D;
use gleam::gl; use gleam::gl;
use gleam::gl::types::{GLint, GLsizei}; use gleam::gl::types::{GLsizei};
use util::task::spawn_named; use util::task::spawn_named;
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::sync::mpsc::{channel, Sender}; use std::sync::mpsc::{channel, Sender};
use util::vec::byte_swap; 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 { pub struct WebGLPaintTask {
size: Size2D<i32>, 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 { impl WebGLPaintTask {
fn new(size: Size2D<i32>) -> WebGLPaintTask { fn new(size: Size2D<i32>) -> Result<WebGLPaintTask, &'static str> {
WebGLPaintTask::create(size); let context = try!(create_offscreen_context(size, GLContextAttributes::default()));
WebGLPaintTask { Ok(WebGLPaintTask {
size: size, 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 (chan, port) = channel::<CanvasMsg>();
let mut painter = try!(WebGLPaintTask::new(size));
spawn_named("WebGLTask".to_owned(), move || { spawn_named("WebGLTask".to_owned(), move || {
let mut painter = WebGLPaintTask::new(size);
painter.init(); painter.init();
loop { loop {
match port.recv().unwrap() { match port.recv().unwrap() {
@ -52,35 +89,8 @@ impl WebGLPaintTask {
} }
} }
}); });
chan
}
fn create(size: Size2D<i32>) { Ok(chan)
// 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) { fn clear(&self, mask: u32) {
@ -95,9 +105,9 @@ impl WebGLPaintTask {
// FIXME(#5652, dmarcos) Instead of a readback strategy we have // FIXME(#5652, dmarcos) Instead of a readback strategy we have
// to layerize the canvas // to layerize the canvas
let mut pixels = gl::read_pixels(0, 0, let mut pixels = gl::read_pixels(0, 0,
self.size.width as gl::GLsizei, self.size.width as gl::GLsizei,
self.size.height as gl::GLsizei, self.size.height as gl::GLsizei,
gl::RGBA, gl::UNSIGNED_BYTE); gl::RGBA, gl::UNSIGNED_BYTE);
// rgba -> bgra // rgba -> bgra
byte_swap(&mut pixels); byte_swap(&mut pixels);
@ -105,8 +115,19 @@ impl WebGLPaintTask {
} }
fn recreate(&mut self, size: Size2D<i32>) { fn recreate(&mut self, size: Size2D<i32>) {
self.size = size; // TODO(ecoal95): GLContext should support a resize() method
self.init(); 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> { fn GetContext(self, id: DOMString) -> Option<CanvasRenderingContext2DOrWebGLRenderingContext> {
match &*id { match &*id {
"2d" => { "2d" => {
let context_2d = self.context_2d.or_init(|| { if self.context_webgl.get().is_some() {
let window = window_from_node(self).root(); debug!("Trying to get a 2d context for a canvas with an already initialized WebGL context");
let size = self.get_size(); return None;
CanvasRenderingContext2D::new(GlobalRef::Window(window.r()), self, size) }
});
Some(CanvasRenderingContext2DOrWebGLRenderingContext::eCanvasRenderingContext2D(Unrooted::from_temporary(context_2d))) let context_2d = self.context_2d.or_init(|| {
} let window = window_from_node(self).root();
"webgl" | "experimental-webgl" => { let size = self.get_size();
let context_webgl = self.context_webgl.or_init(|| { CanvasRenderingContext2D::new(GlobalRef::Window(window.r()), self, size)
let window = window_from_node(self).root(); });
let size = self.get_size(); Some(CanvasRenderingContext2DOrWebGLRenderingContext::eCanvasRenderingContext2D(Unrooted::from_temporary(context_2d)))
WebGLRenderingContext::new(GlobalRef::Window(window.r()), self, size) }
}); "webgl" | "experimental-webgl" => {
Some(CanvasRenderingContext2DOrWebGLRenderingContext::eWebGLRenderingContext(Unrooted::from_temporary(context_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 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 value = attr.value();
let recreate = match attr.local_name() { let recreate = match attr.local_name() {
&atom!("width") => { &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 true
} }
&atom!("height") => { &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 true
} }
_ => false, _ => false,

View file

@ -23,25 +23,32 @@ pub struct WebGLRenderingContext {
impl WebGLRenderingContext { impl WebGLRenderingContext {
fn new_inherited(global: GlobalRef, canvas: JSRef<HTMLCanvasElement>, size: Size2D<i32>) fn new_inherited(global: GlobalRef, canvas: JSRef<HTMLCanvasElement>, size: Size2D<i32>)
-> WebGLRenderingContext { -> Result<WebGLRenderingContext, &'static str> {
WebGLRenderingContext { let chan = try!(WebGLPaintTask::start(size));
Ok(WebGLRenderingContext {
reflector_: Reflector::new(), reflector_: Reflector::new(),
global: GlobalField::from_rooted(&global), global: GlobalField::from_rooted(&global),
renderer: WebGLPaintTask::start(size), renderer: chan,
canvas: JS::from_rooted(canvas), canvas: JS::from_rooted(canvas),
} })
} }
pub fn new(global: GlobalRef, canvas: JSRef<HTMLCanvasElement>, size: Size2D<i32>) pub fn new(global: GlobalRef, canvas: JSRef<HTMLCanvasElement>, size: Size2D<i32>)
-> Temporary<WebGLRenderingContext> { -> Option<Temporary<WebGLRenderingContext>> {
reflect_dom_object(box WebGLRenderingContext::new_inherited(global, canvas, size), match WebGLRenderingContext::new_inherited(global, canvas, size) {
global, WebGLRenderingContextBinding::Wrap) 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>) { pub fn recreate(&self, size: Size2D<i32>) {
self.renderer.send(CanvasMsg::Common(CanvasCommonMsg::Recreate(size))).unwrap(); self.renderer.send(CanvasMsg::Common(CanvasCommonMsg::Recreate(size))).unwrap();
} }
} }
#[unsafe_destructor] #[unsafe_destructor]
@ -72,4 +79,3 @@ impl LayoutCanvasWebGLRenderingContextHelpers for LayoutJS<WebGLRenderingContext
(*self.unsafe_get()).renderer.clone() (*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 * 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/. */ * 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; typedef (CanvasRenderingContext2D or WebGLRenderingContext) RenderingContext;
interface HTMLCanvasElement : HTMLElement { interface HTMLCanvasElement : HTMLElement {

View file

@ -34,7 +34,7 @@ version = "0.0.1"
[[package]] [[package]]
name = "android_glue" name = "android_glue"
version = "0.0.1" 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]] [[package]]
name = "azure" name = "azure"
@ -67,7 +67,7 @@ dependencies = [
"gfx 0.0.1", "gfx 0.0.1",
"gleam 0.0.1 (git+https://github.com/servo/gleam)", "gleam 0.0.1 (git+https://github.com/servo/gleam)",
"glutin 0.0.7 (git+https://github.com/servo/glutin?branch=servo)", "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", "util 0.0.1",
] ]
@ -318,7 +318,7 @@ name = "gdi32-sys"
version = "0.0.4" version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ 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]] [[package]]
@ -517,7 +517,7 @@ name = "kernel32-sys"
version = "0.0.11" version = "0.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ 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]] [[package]]
@ -709,6 +709,19 @@ dependencies = [
"util 0.0.1", "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]] [[package]]
name = "openssl" name = "openssl"
version = "0.5.1" version = "0.5.1"
@ -1000,7 +1013,7 @@ name = "user32-sys"
version = "0.0.11" version = "0.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ 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]] [[package]]
@ -1082,11 +1095,6 @@ dependencies = [
"rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "winapi" name = "winapi"
version = "0.1.15" version = "0.1.15"

15
ports/cef/Cargo.lock generated
View file

@ -65,7 +65,7 @@ dependencies = [
"gfx 0.0.1", "gfx 0.0.1",
"gleam 0.0.1 (git+https://github.com/servo/gleam)", "gleam 0.0.1 (git+https://github.com/servo/gleam)",
"glutin 0.0.7 (git+https://github.com/servo/glutin?branch=servo)", "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", "util 0.0.1",
] ]
@ -687,6 +687,19 @@ dependencies = [
"util 0.0.1", "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]] [[package]]
name = "openssl" name = "openssl"
version = "0.5.1" version = "0.5.1"

15
ports/gonk/Cargo.lock generated
View file

@ -59,7 +59,7 @@ dependencies = [
"gfx 0.0.1", "gfx 0.0.1",
"gleam 0.0.1 (git+https://github.com/servo/gleam)", "gleam 0.0.1 (git+https://github.com/servo/gleam)",
"glutin 0.0.7 (git+https://github.com/servo/glutin?branch=servo)", "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", "util 0.0.1",
] ]
@ -679,6 +679,19 @@ dependencies = [
"malloc_buf 0.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "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]] [[package]]
name = "openssl" name = "openssl"
version = "0.5.1" version = "0.5.1"

@ -1 +1 @@
Subproject commit 5a68056599fb498b0cf3715fd359894825e3b856 Subproject commit 34f588aace4d05ce42f4ca605d5470df8fdd236e