Skip to content

Commit

Permalink
Kick off WebGL 2.0 implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
MortimerGoro committed Oct 27, 2017
1 parent fd4843a commit ddd6c86
Show file tree
Hide file tree
Showing 14 changed files with 1,665 additions and 68 deletions.
35 changes: 24 additions & 11 deletions components/canvas/gl_context.rs
Expand Up @@ -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::webgl::WebGLCommand;
use canvas_traits::webgl::{WebGLCommand, WebGLVersion};
use compositing::compositor_thread::{CompositorProxy, self};
use euclid::Size2D;
use gleam::gl;
Expand Down Expand Up @@ -41,17 +41,20 @@ impl GLContextFactory {
}

/// Creates a new shared GLContext with the main GLContext
pub fn new_shared_context(&self,
size: Size2D<i32>,
attributes: GLContextAttributes) -> Result<GLContextWrapper, &'static str> {
pub fn new_shared_context(
&self,
webgl_version: WebGLVersion,
size: Size2D<i32>,
attributes: GLContextAttributes
) -> Result<GLContextWrapper, &'static str> {
match *self {
GLContextFactory::Native(ref handle, ref dispatcher) => {
let dispatcher = dispatcher.as_ref().map(|d| Box::new(d.clone()) as Box<_>);
let ctx = GLContext::<NativeGLContext>::new_shared_with_dispatcher(size,
attributes,
ColorAttachmentType::Texture,
gl::GlType::default(),
GLVersion::Major(2),
Self::gl_version(webgl_version),
Some(handle),
dispatcher);
ctx.map(GLContextWrapper::Native)
Expand All @@ -61,7 +64,7 @@ impl GLContextFactory {
attributes,
ColorAttachmentType::Texture,
gl::GlType::default(),
GLVersion::Major(2),
Self::gl_version(webgl_version),
Some(handle),
None);
ctx.map(GLContextWrapper::OSMesa)
Expand All @@ -70,16 +73,19 @@ impl GLContextFactory {
}

/// Creates a new non-shared GLContext
pub fn new_context(&self,
size: Size2D<i32>,
attributes: GLContextAttributes) -> Result<GLContextWrapper, &'static str> {
pub fn new_context(
&self,
webgl_version: WebGLVersion,
size: Size2D<i32>,
attributes: GLContextAttributes
) -> Result<GLContextWrapper, &'static str> {
match *self {
GLContextFactory::Native(..) => {
let ctx = GLContext::<NativeGLContext>::new_shared_with_dispatcher(size,
attributes,
ColorAttachmentType::Texture,
gl::GlType::default(),
GLVersion::Major(2),
Self::gl_version(webgl_version),
None,
None);
ctx.map(GLContextWrapper::Native)
Expand All @@ -89,13 +95,20 @@ impl GLContextFactory {
attributes,
ColorAttachmentType::Texture,
gl::GlType::default(),
GLVersion::Major(2),
Self::gl_version(webgl_version),
None,
None);
ctx.map(GLContextWrapper::OSMesa)
}
}
}

fn gl_version(webgl_version: WebGLVersion) -> GLVersion {
match webgl_version {
WebGLVersion::WebGL1 => GLVersion::Major(2),
WebGLVersion::WebGL2 => GLVersion::Major(3),
}
}
}


Expand Down
9 changes: 5 additions & 4 deletions components/canvas/webgl_thread.rs
Expand Up @@ -89,8 +89,8 @@ impl<VR: WebVRRenderHandler + 'static, OB: WebGLThreadObserver> WebGLThread<VR,
#[inline]
fn handle_msg(&mut self, msg: WebGLMsg, webgl_chan: &WebGLChan) -> bool {
match msg {
WebGLMsg::CreateContext(size, attributes, result_sender) => {
let result = self.create_webgl_context(size, attributes);
WebGLMsg::CreateContext(version, size, attributes, result_sender) => {
let result = self.create_webgl_context(version, size, attributes);
result_sender.send(result.map(|(id, limits, share_mode)|
WebGLCreateContextResult {
sender: WebGLMsgSender::new(id, webgl_chan.clone()),
Expand Down Expand Up @@ -179,15 +179,16 @@ impl<VR: WebVRRenderHandler + 'static, OB: WebGLThreadObserver> WebGLThread<VR,

/// Creates a new WebGLContext
fn create_webgl_context(&mut self,
version: WebGLVersion,
size: Size2D<i32>,
attributes: GLContextAttributes)
-> Result<(WebGLContextId, GLLimits, WebGLContextShareMode), String> {
// First try to create a shared context for the best performance.
// Fallback to readback mode if the shared context creation fails.
let result = self.gl_factory.new_shared_context(size, attributes)
let result = self.gl_factory.new_shared_context(version, size, attributes)
.map(|r| (r, WebGLContextShareMode::SharedTexture))
.or_else(|_| {
let ctx = self.gl_factory.new_context(size, attributes);
let ctx = self.gl_factory.new_context(version, size, attributes);
ctx.map(|r| (r, WebGLContextShareMode::Readback))
});

Expand Down
14 changes: 13 additions & 1 deletion components/canvas_traits/webgl.rs
Expand Up @@ -25,7 +25,8 @@ pub use ::webgl_channel::WebGLChan;
#[derive(Clone, Deserialize, Serialize)]
pub enum WebGLMsg {
/// Creates a new WebGLContext.
CreateContext(Size2D<i32>, GLContextAttributes, WebGLSender<Result<(WebGLCreateContextResult), String>>),
CreateContext(WebGLVersion, Size2D<i32>, GLContextAttributes,
WebGLSender<Result<(WebGLCreateContextResult), String>>),
/// Resizes a WebGLContext.
ResizeContext(WebGLContextId, Size2D<i32>, WebGLSender<Result<(), String>>),
/// Drops a WebGLContext.
Expand Down Expand Up @@ -72,6 +73,17 @@ pub enum WebGLContextShareMode {
Readback,
}

/// Defines the WebGL version
#[derive(Clone, Copy, Deserialize, MallocSizeOf, Serialize)]
pub enum WebGLVersion {
/// https://www.khronos.org/registry/webgl/specs/1.0.2/
/// Conforms closely to the OpenGL ES 2.0 API
WebGL1,
/// https://www.khronos.org/registry/webgl/specs/latest/2.0/
/// Conforms closely to the OpenGL ES 3.0 API
WebGL2,
}

/// Helper struct to send WebGLCommands to a specific WebGLContext.
#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
pub struct WebGLMsgSender {
Expand Down
4 changes: 4 additions & 0 deletions components/config/prefs.rs
Expand Up @@ -277,4 +277,8 @@ impl Preferences {
pub fn is_dom_to_texture_enabled(&self) -> bool {
self.get("dom.webgl.dom_to_texture.enabled").as_boolean().unwrap_or(false)
}

pub fn is_webgl2_enabled(&self) -> bool {
self.get("dom.webgl2.enabled").as_boolean().unwrap_or(false)
}
}
3 changes: 2 additions & 1 deletion components/script/dom/bindings/trace.rs
Expand Up @@ -33,7 +33,7 @@ use app_units::Au;
use canvas_traits::canvas::{CanvasGradientStop, LinearGradientStyle, RadialGradientStyle};
use canvas_traits::canvas::{CompositionOrBlending, LineCapStyle, LineJoinStyle, RepetitionStyle};
use canvas_traits::webgl::{WebGLBufferId, WebGLFramebufferId, WebGLProgramId, WebGLRenderbufferId};
use canvas_traits::webgl::{WebGLChan, WebGLContextShareMode, WebGLError, WebGLPipeline, WebGLMsgSender};
use canvas_traits::webgl::{WebGLChan, WebGLContextShareMode, WebGLError, WebGLPipeline, WebGLMsgSender, WebGLVersion};
use canvas_traits::webgl::{WebGLReceiver, WebGLSender, WebGLShaderId, WebGLTextureId, WebGLVertexArrayId};
use cssparser::RGBA;
use devtools_traits::{CSSError, TimelineMarkerType, WorkerId};
Expand Down Expand Up @@ -411,6 +411,7 @@ unsafe_no_jsmanaged_fields!(WebGLRenderbufferId);
unsafe_no_jsmanaged_fields!(WebGLShaderId);
unsafe_no_jsmanaged_fields!(WebGLTextureId);
unsafe_no_jsmanaged_fields!(WebGLVertexArrayId);
unsafe_no_jsmanaged_fields!(WebGLVersion);
unsafe_no_jsmanaged_fields!(MediaList);
unsafe_no_jsmanaged_fields!(WebVRGamepadHand);
unsafe_no_jsmanaged_fields!(ScriptToConstellationChan);
Expand Down
107 changes: 79 additions & 28 deletions components/script/dom/htmlcanvaselement.rs
Expand Up @@ -4,13 +4,13 @@

use base64;
use canvas_traits::canvas::{CanvasMsg, FromScriptMsg};
use canvas_traits::webgl::WebGLVersion;
use dom::attr::Attr;
use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods;
use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding;
use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::HTMLCanvasElementMethods;
use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::{HTMLCanvasElementMethods, RenderingContext};
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes;
use dom::bindings::codegen::UnionTypes::CanvasRenderingContext2DOrWebGLRenderingContext;
use dom::bindings::conversions::ConversionResult;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::inheritance::Castable;
Expand All @@ -24,6 +24,7 @@ use dom::globalscope::GlobalScope;
use dom::htmlelement::HTMLElement;
use dom::node::{Node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use dom::webgl2renderingcontext::WebGL2RenderingContext;
use dom::webglrenderingcontext::{LayoutCanvasWebGLRenderingContextHelpers, WebGLRenderingContext};
use dom_struct::dom_struct;
use euclid::Size2D;
Expand All @@ -35,6 +36,7 @@ use js::error::throw_type_error;
use js::jsapi::{HandleValue, JSContext};
use offscreen_gl_context::GLContextAttributes;
use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource};
use servo_config::prefs::PREFS;
use std::iter::repeat;
use style::attr::{AttrValue, LengthOrPercentageOrAuto};

Expand All @@ -46,6 +48,7 @@ const DEFAULT_HEIGHT: u32 = 150;
pub enum CanvasContext {
Context2d(Dom<CanvasRenderingContext2D>),
WebGL(Dom<WebGLRenderingContext>),
WebGL2(Dom<WebGL2RenderingContext>),
}

#[dom_struct]
Expand Down Expand Up @@ -79,6 +82,7 @@ impl HTMLCanvasElement {
match *context {
CanvasContext::Context2d(ref context) => context.set_bitmap_dimensions(size),
CanvasContext::WebGL(ref context) => context.recreate(size),
CanvasContext::WebGL2(ref context) => context.recreate(size),
}
}
}
Expand Down Expand Up @@ -113,6 +117,9 @@ impl LayoutHTMLCanvasElementHelpers for LayoutDom<HTMLCanvasElement> {
Some(&CanvasContext::WebGL(ref context)) => {
context.to_layout().canvas_data_source()
},
Some(&CanvasContext::WebGL2(ref context)) => {
context.to_layout().canvas_data_source()
},
None => {
HTMLCanvasDataSource::Image(None)
}
Expand Down Expand Up @@ -165,32 +172,16 @@ impl HTMLCanvasElement {
}
}

#[allow(unsafe_code)]
pub fn get_or_init_webgl_context(&self,
cx: *mut JSContext,
attrs: Option<HandleValue>) -> Option<DomRoot<WebGLRenderingContext>> {
pub fn get_or_init_webgl_context(
&self,
cx: *mut JSContext,
attrs: Option<HandleValue>
) -> Option<DomRoot<WebGLRenderingContext>> {
if self.context.borrow().is_none() {
let window = window_from_node(self);
let size = self.get_size();

let attrs = if let Some(webgl_attributes) = attrs {
match unsafe {
WebGLContextAttributes::new(cx, webgl_attributes) } {
Ok(ConversionResult::Success(ref attrs)) => From::from(attrs),
Ok(ConversionResult::Failure(ref error)) => {
unsafe { throw_type_error(cx, &error); }
return None;
}
_ => {
debug!("Unexpected error on conversion of WebGLContextAttributes");
return None;
}
}
} else {
GLContextAttributes::default()
};

let maybe_ctx = WebGLRenderingContext::new(&window, self, size, attrs);
let attrs = Self::get_gl_attributes(cx, attrs)?;
let maybe_ctx = WebGLRenderingContext::new(&window, self, WebGLVersion::WebGL1, size, attrs);

*self.context.borrow_mut() = maybe_ctx.map( |ctx| CanvasContext::WebGL(Dom::from_ref(&*ctx)));
}
Expand All @@ -202,6 +193,58 @@ impl HTMLCanvasElement {
}
}

pub fn get_or_init_webgl2_context(
&self,
cx: *mut JSContext,
attrs: Option<HandleValue>
) -> Option<DomRoot<WebGL2RenderingContext>> {
if !PREFS.is_webgl2_enabled() {
return None
}
if self.context.borrow().is_none() {
let window = window_from_node(self);
let size = self.get_size();
let attrs = Self::get_gl_attributes(cx, attrs)?;
let maybe_ctx = WebGL2RenderingContext::new(&window, self, size, attrs);

*self.context.borrow_mut() = maybe_ctx.map( |ctx| CanvasContext::WebGL2(Dom::from_ref(&*ctx)));
}

if let Some(CanvasContext::WebGL2(ref context)) = *self.context.borrow() {
Some(DomRoot::from_ref(&*context))
} else {
None
}
}

/// Gets the base WebGLRenderingContext for WebGL or WebGL 2, if exists.
pub fn get_base_webgl_context(&self) -> Option<DomRoot<WebGLRenderingContext>> {
match *self.context.borrow() {
Some(CanvasContext::WebGL(ref context)) => Some(DomRoot::from_ref(&*context)),
Some(CanvasContext::WebGL2(ref context)) => Some(context.base_context()),
_ => None
}
}

#[allow(unsafe_code)]
fn get_gl_attributes(cx: *mut JSContext, attrs: Option<HandleValue>) -> Option<GLContextAttributes> {
let webgl_attributes = match attrs {
Some(attrs) => attrs,
None => return Some(GLContextAttributes::default()),
};
match unsafe { WebGLContextAttributes::new(cx, webgl_attributes) } {
Ok(ConversionResult::Success(ref attrs)) => Some(From::from(attrs)),
Ok(ConversionResult::Failure(ref error)) => {
unsafe { throw_type_error(cx, &error); }
None
}
_ => {
debug!("Unexpected error on conversion of WebGLContextAttributes");
None
}
}
}

pub fn is_valid(&self) -> bool {
self.Height() != 0 && self.Width() != 0
}
Expand All @@ -225,6 +268,10 @@ impl HTMLCanvasElement {
// TODO: add a method in WebGLRenderingContext to get the pixels.
return None;
},
Some(&CanvasContext::WebGL2(_)) => {
// TODO: add a method in WebGL2RenderingContext to get the pixels.
return None;
},
None => {
repeat(0xffu8).take((size.height as usize) * (size.width as usize) * 4).collect()
}
Expand Down Expand Up @@ -253,15 +300,19 @@ impl HTMLCanvasElementMethods for HTMLCanvasElement {
cx: *mut JSContext,
id: DOMString,
attributes: Vec<HandleValue>)
-> Option<CanvasRenderingContext2DOrWebGLRenderingContext> {
-> Option<RenderingContext> {
match &*id {
"2d" => {
self.get_or_init_2d_context()
.map(CanvasRenderingContext2DOrWebGLRenderingContext::CanvasRenderingContext2D)
.map(RenderingContext::CanvasRenderingContext2D)
}
"webgl" | "experimental-webgl" => {
self.get_or_init_webgl_context(cx, attributes.get(0).cloned())
.map(CanvasRenderingContext2DOrWebGLRenderingContext::WebGLRenderingContext)
.map(RenderingContext::WebGLRenderingContext)
}
"webgl2" | "experimental-webgl2" => {
self.get_or_init_webgl2_context(cx, attributes.get(0).cloned())
.map(RenderingContext::WebGL2RenderingContext)
}
_ => None
}
Expand Down
1 change: 1 addition & 0 deletions components/script/dom/mod.rs
Expand Up @@ -467,6 +467,7 @@ pub mod vrpose;
pub mod vrstageparameters;
pub mod webgl_extensions;
pub use self::webgl_extensions::ext::*;
pub mod webgl2renderingcontext;
pub mod webgl_validations;
pub mod webglactiveinfo;
pub mod webglbuffer;
Expand Down

0 comments on commit ddd6c86

Please sign in to comment.