diff options
Diffstat (limited to 'components/canvas')
-rw-r--r-- | components/canvas/Cargo.toml | 3 | ||||
-rw-r--r-- | components/canvas/canvas_paint_thread.rs | 16 | ||||
-rw-r--r-- | components/canvas/gl_context.rs | 203 | ||||
-rw-r--r-- | components/canvas/lib.rs | 10 | ||||
-rw-r--r-- | components/canvas/webgl_mode/inprocess.rs | 95 | ||||
-rw-r--r-- | components/canvas/webgl_mode/mod.rs | 6 | ||||
-rw-r--r-- | components/canvas/webgl_paint_thread.rs | 379 | ||||
-rw-r--r-- | components/canvas/webgl_thread.rs | 1204 |
8 files changed, 1521 insertions, 395 deletions
diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index ad149daccb6..e8bf234a052 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -12,6 +12,7 @@ path = "lib.rs" [dependencies] azure = {git = "https://github.com/servo/rust-azure"} canvas_traits = {path = "../canvas_traits"} +compositing = {path = "../compositing"} cssparser = "0.19" euclid = "0.15" gleam = "0.4" @@ -19,5 +20,5 @@ ipc-channel = "0.8" log = "0.3.5" num-traits = "0.1.32" offscreen_gl_context = { version = "0.11", features = ["serde"] } -servo_config = {path = "../config"} +webrender = {git = "https://github.com/servo/webrender"} webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]} diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs index b7e21777001..8fe05d1f6a9 100644 --- a/components/canvas/canvas_paint_thread.rs +++ b/components/canvas/canvas_paint_thread.rs @@ -8,7 +8,7 @@ use azure::azure_hl::{BackendType, DrawOptions, DrawTarget, Pattern, StrokeOptio use azure::azure_hl::{Color, ColorPattern, DrawSurfaceOptions, Filter, PathBuilder}; use azure::azure_hl::{ExtendMode, GradientStop, LinearGradientPattern, RadialGradientPattern}; use azure::azure_hl::SurfacePattern; -use canvas_traits::*; +use canvas_traits::canvas::*; use cssparser::RGBA; use euclid::{Transform2D, Point2D, Vector2D, Rect, Size2D}; use ipc_channel::ipc::{self, IpcSender}; @@ -193,12 +193,8 @@ impl<'a> CanvasPaintThread<'a> { Canvas2dMsg::SetShadowColor(ref color) => painter.set_shadow_color(color.to_azure_style()), } }, - CanvasMsg::Common(message) => { - match message { - CanvasCommonMsg::Close => break, - CanvasCommonMsg::Recreate(size) => painter.recreate(size), - } - }, + CanvasMsg::Close => break, + CanvasMsg::Recreate(size) => painter.recreate(size), CanvasMsg::FromScript(message) => { match message { FromScriptMsg::SendPixels(chan) => { @@ -213,8 +209,6 @@ impl<'a> CanvasPaintThread<'a> { } } } - CanvasMsg::WebGL(_) => panic!("Wrong WebGL message sent to Canvas2D thread"), - CanvasMsg::WebVR(_) => panic!("Wrong WebVR message sent to Canvas2D thread"), } } }).expect("Thread spawning failed"); @@ -571,7 +565,7 @@ impl<'a> CanvasPaintThread<'a> { }) } - fn send_data(&mut self, chan: IpcSender<CanvasData>) { + fn send_data(&mut self, chan: IpcSender<CanvasImageData>) { self.drawtarget.snapshot().get_data_surface().with_data(|element| { let size = self.drawtarget.get_size(); @@ -614,7 +608,7 @@ impl<'a> CanvasPaintThread<'a> { let data = CanvasImageData { image_key: self.image_key.unwrap(), }; - chan.send(CanvasData::Image(data)).unwrap(); + chan.send(data).unwrap(); }) } diff --git a/components/canvas/gl_context.rs b/components/canvas/gl_context.rs new file mode 100644 index 00000000000..69a26c0e03c --- /dev/null +++ b/components/canvas/gl_context.rs @@ -0,0 +1,203 @@ +/* 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_traits::webgl::WebGLCommand; +use compositing::compositor_thread::{CompositorProxy, self}; +use euclid::Size2D; +use gleam::gl; +use offscreen_gl_context::{ColorAttachmentType, GLContext, GLContextAttributes, GLContextDispatcher, GLLimits}; +use offscreen_gl_context::{NativeGLContext, NativeGLContextHandle, NativeGLContextMethods}; +use offscreen_gl_context::{OSMesaContext, OSMesaContextHandle}; +use std::sync::{Arc, Mutex}; +use super::webgl_thread::WebGLImpl; + +/// The GLContextFactory is used to create shared GL contexts with the main thread GL context. +/// Currently, shared textures are used to render WebGL textures into the WR compositor. +/// In order to create a shared context, the GLContextFactory stores the handle of the main GL context. +pub enum GLContextFactory { + Native(NativeGLContextHandle, Option<MainThreadDispatcher>), + OSMesa(OSMesaContextHandle), +} + +impl GLContextFactory { + /// Creates a new GLContextFactory that uses the currently bound GL context to create shared contexts. + pub fn current_native_handle(proxy: &CompositorProxy) -> Option<GLContextFactory> { + NativeGLContext::current_handle().map(|handle| { + if cfg!(target_os = "windows") { + // Used to dispatch functions from the GLContext thread to the main thread's event loop. + // Required to allow WGL GLContext sharing in Windows. + GLContextFactory::Native(handle, Some(MainThreadDispatcher::new(proxy.clone_compositor_proxy()))) + } else { + GLContextFactory::Native(handle, None) + } + }) + } + + /// Creates a new GLContextFactory that uses the currently bound OSMesa context to create shared contexts. + pub fn current_osmesa_handle() -> Option<GLContextFactory> { + OSMesaContext::current_handle().map(GLContextFactory::OSMesa) + } + + /// Creates a new shared GLContext with the main GLContext + pub fn new_shared_context(&self, + 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(), + Some(handle), + dispatcher); + ctx.map(GLContextWrapper::Native) + } + GLContextFactory::OSMesa(ref handle) => { + let ctx = GLContext::<OSMesaContext>::new_shared_with_dispatcher(size.to_untyped(), + attributes, + ColorAttachmentType::Texture, + gl::GlType::default(), + Some(handle), + None); + ctx.map(GLContextWrapper::OSMesa) + } + } + } + + /// Creates a new non-shared GLContext + pub fn new_context(&self, + 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(), + None, + None); + ctx.map(GLContextWrapper::Native) + } + GLContextFactory::OSMesa(_) => { + let ctx = GLContext::<OSMesaContext>::new_shared_with_dispatcher(size.to_untyped(), + attributes, + ColorAttachmentType::Texture, + gl::GlType::default(), + None, + None); + ctx.map(GLContextWrapper::OSMesa) + } + } + } +} + + +/// GLContextWrapper used to abstract NativeGLContext and OSMesaContext types +pub enum GLContextWrapper { + Native(GLContext<NativeGLContext>), + OSMesa(GLContext<OSMesaContext>), +} + +impl GLContextWrapper { + pub fn make_current(&self) { + match *self { + GLContextWrapper::Native(ref ctx) => { + ctx.make_current().unwrap(); + } + GLContextWrapper::OSMesa(ref ctx) => { + ctx.make_current().unwrap(); + } + } + } + + pub fn unbind(&self) { + match *self { + GLContextWrapper::Native(ref ctx) => { + ctx.unbind().unwrap(); + } + GLContextWrapper::OSMesa(ref ctx) => { + ctx.unbind().unwrap(); + } + } + } + + pub fn apply_command(&self, cmd: WebGLCommand) { + match *self { + GLContextWrapper::Native(ref ctx) => { + WebGLImpl::apply(ctx, cmd); + } + GLContextWrapper::OSMesa(ref ctx) => { + WebGLImpl::apply(ctx, cmd); + } + } + } + + pub fn gl(&self) -> &gl::Gl { + match *self { + GLContextWrapper::Native(ref ctx) => { + ctx.gl() + } + GLContextWrapper::OSMesa(ref ctx) => { + ctx.gl() + } + } + } + + pub fn get_info(&self) -> (Size2D<i32>, u32, GLLimits) { + match *self { + GLContextWrapper::Native(ref ctx) => { + let (real_size, texture_id) = { + let draw_buffer = ctx.borrow_draw_buffer().unwrap(); + (draw_buffer.size(), draw_buffer.get_bound_texture_id().unwrap()) + }; + + let limits = ctx.borrow_limits().clone(); + + (real_size, texture_id, limits) + } + GLContextWrapper::OSMesa(ref ctx) => { + let (real_size, texture_id) = { + let draw_buffer = ctx.borrow_draw_buffer().unwrap(); + (draw_buffer.size(), draw_buffer.get_bound_texture_id().unwrap()) + }; + + let limits = ctx.borrow_limits().clone(); + + (real_size, texture_id, limits) + } + } + } + + pub fn resize(&mut self, size: Size2D<i32>) -> Result<(), &'static str> { + match *self { + GLContextWrapper::Native(ref mut ctx) => { + ctx.resize(size) + } + GLContextWrapper::OSMesa(ref mut ctx) => { + ctx.resize(size) + } + } + } +} + +/// Implements GLContextDispatcher to dispatch functions from GLContext threads to the main thread's event loop. +/// It's used in Windows to allow WGL GLContext sharing. +#[derive(Clone)] +pub struct MainThreadDispatcher { + compositor_proxy: Arc<Mutex<CompositorProxy>> +} + +impl MainThreadDispatcher { + fn new(proxy: CompositorProxy) -> Self { + Self { + compositor_proxy: Arc::new(Mutex::new(proxy)), + } + } +} +impl GLContextDispatcher for MainThreadDispatcher { + fn dispatch(&self, f: Box<Fn() + Send>) { + self.compositor_proxy.lock().unwrap().send(compositor_thread::Msg::Dispatch(f)); + } +} diff --git a/components/canvas/lib.rs b/components/canvas/lib.rs index a3d90909637..b9f0823a07d 100644 --- a/components/canvas/lib.rs +++ b/components/canvas/lib.rs @@ -6,16 +6,18 @@ extern crate azure; extern crate canvas_traits; +extern crate compositing; extern crate cssparser; extern crate euclid; extern crate gleam; extern crate ipc_channel; -#[macro_use] -extern crate log; +#[macro_use] extern crate log; extern crate num_traits; extern crate offscreen_gl_context; -extern crate servo_config; +extern crate webrender; extern crate webrender_api; pub mod canvas_paint_thread; -pub mod webgl_paint_thread; +pub mod gl_context; +mod webgl_mode; +pub mod webgl_thread; diff --git a/components/canvas/webgl_mode/inprocess.rs b/components/canvas/webgl_mode/inprocess.rs new file mode 100644 index 00000000000..257f1395545 --- /dev/null +++ b/components/canvas/webgl_mode/inprocess.rs @@ -0,0 +1,95 @@ +/* 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 ::gl_context::GLContextFactory; +use ::webgl_thread::{WebGLExternalImageApi, WebGLExternalImageHandler, WebGLThreadObserver, WebGLThread}; +use canvas_traits::webgl::{WebGLChan, WebGLContextId, WebGLMsg, WebGLPipeline, WebGLReceiver}; +use canvas_traits::webgl::{WebGLSender, WebVRCommand, WebVRRenderHandler}; +use canvas_traits::webgl::webgl_channel; +use euclid::Size2D; +use std::marker::PhantomData; +use webrender; +use webrender_api; + +/// WebGL Threading API entry point that lives in the constellation. +pub struct WebGLThreads(WebGLSender<WebGLMsg>); + +impl WebGLThreads { + /// Creates a new WebGLThreads object + pub fn new(gl_factory: GLContextFactory, + webrender_api_sender: webrender_api::RenderApiSender, + webvr_compositor: Option<Box<WebVRRenderHandler>>) + -> (WebGLThreads, Box<webrender::ExternalImageHandler>) { + // This implementation creates a single `WebGLThread` for all the pipelines. + let channel = WebGLThread::start(gl_factory, + webrender_api_sender, + webvr_compositor.map(|c| WebVRRenderWrapper(c)), + PhantomData); + let external = WebGLExternalImageHandler::new(WebGLExternalImages::new(channel.clone())); + (WebGLThreads(channel), Box::new(external)) + } + + /// Gets the WebGLThread handle for each script pipeline. + pub fn pipeline(&self) -> WebGLPipeline { + // This mode creates a single thread, so the existing WebGLChan is just cloned. + WebGLPipeline(WebGLChan(self.0.clone())) + } + + /// Sends a exit message to close the WebGLThreads and release all WebGLContexts. + pub fn exit(&self) -> Result<(), &'static str> { + self.0.send(WebGLMsg::Exit).map_err(|_| "Failed to send Exit message") + } +} + +/// Bridge between the webrender::ExternalImage callbacks and the WebGLThreads. +struct WebGLExternalImages { + webgl_channel: WebGLSender<WebGLMsg>, + // Used to avoid creating a new channel on each received WebRender request. + lock_channel: (WebGLSender<(u32, Size2D<i32>)>, WebGLReceiver<(u32, Size2D<i32>)>), +} + +impl WebGLExternalImages { + fn new(channel: WebGLSender<WebGLMsg>) -> Self { + Self { + webgl_channel: channel, + lock_channel: webgl_channel().unwrap(), + } + } +} + +impl WebGLExternalImageApi for WebGLExternalImages { + fn lock(&mut self, ctx_id: WebGLContextId) -> (u32, Size2D<i32>) { + self.webgl_channel.send(WebGLMsg::Lock(ctx_id, self.lock_channel.0.clone())).unwrap(); + self.lock_channel.1.recv().unwrap() + } + + fn unlock(&mut self, ctx_id: WebGLContextId) { + self.webgl_channel.send(WebGLMsg::Unlock(ctx_id)).unwrap(); + } +} + +/// Custom observer used in a `WebGLThread`. +impl WebGLThreadObserver for PhantomData<()> { + fn on_context_create(&mut self, ctx_id: WebGLContextId, texture_id: u32, size: Size2D<i32>) { + debug!("WebGLContext created (ctx_id: {:?} texture_id: {:?} size: {:?}", ctx_id, texture_id, size); + } + + fn on_context_resize(&mut self, ctx_id: WebGLContextId, texture_id: u32, size: Size2D<i32>) { + debug!("WebGLContext resized (ctx_id: {:?} texture_id: {:?} size: {:?}", ctx_id, texture_id, size); + } + + fn on_context_delete(&mut self, ctx_id: WebGLContextId) { + debug!("WebGLContext deleted (ctx_id: {:?})", ctx_id); + } +} + + +/// Wrapper to send WebVR commands used in `WebGLThread`. +struct WebVRRenderWrapper(Box<WebVRRenderHandler>); + +impl WebVRRenderHandler for WebVRRenderWrapper { + fn handle(&mut self, command: WebVRCommand, texture: Option<(u32, Size2D<i32>)>) { + self.0.handle(command, texture); + } +} diff --git a/components/canvas/webgl_mode/mod.rs b/components/canvas/webgl_mode/mod.rs new file mode 100644 index 00000000000..660818fb096 --- /dev/null +++ b/components/canvas/webgl_mode/mod.rs @@ -0,0 +1,6 @@ +/* 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/. */ + +mod inprocess; +pub use self::inprocess::WebGLThreads; diff --git a/components/canvas/webgl_paint_thread.rs b/components/canvas/webgl_paint_thread.rs deleted file mode 100644 index 2b6819effba..00000000000 --- a/components/canvas/webgl_paint_thread.rs +++ /dev/null @@ -1,379 +0,0 @@ -/* 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_traits::{CanvasCommonMsg, CanvasData, CanvasMsg, CanvasImageData}; -use canvas_traits::{FromLayoutMsg, FromScriptMsg, byte_swap}; -use euclid::Size2D; -use gleam::gl; -use ipc_channel::ipc::{self, IpcSender}; -use offscreen_gl_context::{ColorAttachmentType, GLContext, GLLimits}; -use offscreen_gl_context::{GLContextAttributes, NativeGLContext, OSMesaContext}; -use servo_config::opts; -use std::borrow::ToOwned; -use std::mem; -use std::sync::Arc; -use std::sync::mpsc::channel; -use std::thread; -use webrender_api; - -enum GLContextWrapper { - Native(GLContext<NativeGLContext>), - OSMesa(GLContext<OSMesaContext>), -} - -impl GLContextWrapper { - fn new(size: Size2D<i32>, - attributes: GLContextAttributes, - gl_type: gl::GlType) -> Result<GLContextWrapper, &'static str> { - if opts::get().should_use_osmesa() { - let ctx = GLContext::<OSMesaContext>::new(size, - attributes, - ColorAttachmentType::Texture, - gl_type, - None); - ctx.map(GLContextWrapper::OSMesa) - } else { - let ctx = GLContext::<NativeGLContext>::new(size, - attributes, - ColorAttachmentType::Texture, - gl_type, - None); - ctx.map(GLContextWrapper::Native) - } - } - - pub fn get_limits(&self) -> GLLimits { - match *self { - GLContextWrapper::Native(ref ctx) => { - ctx.borrow_limits().clone() - } - GLContextWrapper::OSMesa(ref ctx) => { - ctx.borrow_limits().clone() - } - } - } - - fn resize(&mut self, size: Size2D<i32>) -> Result<Size2D<i32>, &'static str> { - match *self { - GLContextWrapper::Native(ref mut ctx) => { - ctx.resize(size)?; - Ok(ctx.borrow_draw_buffer().unwrap().size()) - } - GLContextWrapper::OSMesa(ref mut ctx) => { - ctx.resize(size)?; - Ok(ctx.borrow_draw_buffer().unwrap().size()) - } - } - } - - fn gl(&self) -> &gl::Gl { - match *self { - GLContextWrapper::Native(ref ctx) => { - ctx.gl() - } - GLContextWrapper::OSMesa(ref ctx) => { - ctx.gl() - } - } - } - - pub fn make_current(&self) { - match *self { - GLContextWrapper::Native(ref ctx) => { - ctx.make_current().unwrap(); - } - GLContextWrapper::OSMesa(ref ctx) => { - ctx.make_current().unwrap(); - } - } - } - - pub fn apply_command(&self, cmd: webrender_api::WebGLCommand) { - match *self { - GLContextWrapper::Native(ref ctx) => { - cmd.apply(ctx); - } - GLContextWrapper::OSMesa(ref ctx) => { - cmd.apply(ctx); - } - } - } -} - -enum WebGLPaintTaskData { - WebRender(webrender_api::RenderApi, webrender_api::WebGLContextId), - Readback { - context: GLContextWrapper, - webrender_api: webrender_api::RenderApi, - image_key: Option<webrender_api::ImageKey>, - /// An old webrender image key that can be deleted when the next epoch ends. - old_image_key: Option<webrender_api::ImageKey>, - /// An old webrender image key that can be deleted when the current epoch ends. - very_old_image_key: Option<webrender_api::ImageKey>, - }, -} - -pub struct WebGLPaintThread { - size: Size2D<i32>, - data: WebGLPaintTaskData, -} - -fn create_readback_painter(size: Size2D<i32>, - attrs: GLContextAttributes, - webrender_api: webrender_api::RenderApi, - gl_type: gl::GlType) - -> Result<(WebGLPaintThread, GLLimits), String> { - let context = GLContextWrapper::new(size, attrs, gl_type)?; - let limits = context.get_limits(); - let painter = WebGLPaintThread { - size: size, - data: WebGLPaintTaskData::Readback { - context: context, - webrender_api: webrender_api, - image_key: None, - old_image_key: None, - very_old_image_key: None, - }, - }; - - Ok((painter, limits)) -} - -impl WebGLPaintThread { - fn new(size: Size2D<i32>, - attrs: GLContextAttributes, - webrender_api_sender: webrender_api::RenderApiSender, - gl_type: gl::GlType) - -> Result<(WebGLPaintThread, GLLimits), String> { - let wr_api = webrender_api_sender.create_api(); - let device_size = webrender_api::DeviceIntSize::from_untyped(&size); - match wr_api.request_webgl_context(&device_size, attrs) { - Ok((id, limits)) => { - let painter = WebGLPaintThread { - data: WebGLPaintTaskData::WebRender(wr_api, id), - size: size - }; - Ok((painter, limits)) - }, - Err(msg) => { - warn!("Initial context creation failed, falling back to readback: {}", msg); - create_readback_painter(size, attrs, wr_api, gl_type) - } - } - } - - fn handle_webgl_message(&self, message: webrender_api::WebGLCommand) { - debug!("WebGL message: {:?}", message); - match self.data { - WebGLPaintTaskData::WebRender(ref api, id) => { - api.send_webgl_command(id, message); - } - WebGLPaintTaskData::Readback { ref context, .. } => { - context.apply_command(message); - } - } - } - - fn handle_webvr_message(&self, message: webrender_api::VRCompositorCommand) { - match self.data { - WebGLPaintTaskData::WebRender(ref api, id) => { - api.send_vr_compositor_command(id, message); - } - WebGLPaintTaskData::Readback { .. } => { - error!("Webrender is required for WebVR implementation"); - } - } - } - - - /// Creates a new `WebGLPaintThread` and returns an `IpcSender` to - /// communicate with it. - pub fn start(size: Size2D<i32>, - attrs: GLContextAttributes, - webrender_api_sender: webrender_api::RenderApiSender) - -> Result<(IpcSender<CanvasMsg>, GLLimits), String> { - let (sender, receiver) = ipc::channel::<CanvasMsg>().unwrap(); - let (result_chan, result_port) = channel(); - thread::Builder::new().name("WebGLThread".to_owned()).spawn(move || { - let gl_type = gl::GlType::default(); - let mut painter = match WebGLPaintThread::new(size, attrs, webrender_api_sender, gl_type) { - Ok((thread, limits)) => { - result_chan.send(Ok(limits)).unwrap(); - thread - }, - Err(e) => { - result_chan.send(Err(e)).unwrap(); - return - } - }; - painter.init(); - loop { - match receiver.recv().unwrap() { - CanvasMsg::WebGL(message) => painter.handle_webgl_message(message), - CanvasMsg::Common(message) => { - match message { - CanvasCommonMsg::Close => break, - // TODO(emilio): handle error nicely - CanvasCommonMsg::Recreate(size) => painter.recreate(size).unwrap(), - } - }, - CanvasMsg::FromScript(message) => { - match message { - FromScriptMsg::SendPixels(chan) =>{ - // Read the comment on - // HTMLCanvasElement::fetch_all_data. - chan.send(None).unwrap(); - } - } - } - CanvasMsg::FromLayout(message) => { - match message { - FromLayoutMsg::SendData(chan) => - painter.send_data(chan), - } - } - CanvasMsg::Canvas2d(_) => panic!("Wrong message sent to WebGLThread"), - CanvasMsg::WebVR(message) => painter.handle_webvr_message(message) - } - } - }).expect("Thread spawning failed"); - - result_port.recv().unwrap().map(|limits| (sender, limits)) - } - - fn send_data(&mut self, chan: IpcSender<CanvasData>) { - match self.data { - WebGLPaintTaskData::Readback { - ref context, - ref webrender_api, - ref mut image_key, - ref mut old_image_key, - ref mut very_old_image_key, - } => { - let width = self.size.width as usize; - let height = self.size.height as usize; - - let mut pixels = context.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]); - } - - // rgba -> bgra - byte_swap(&mut pixels); - - let descriptor = webrender_api::ImageDescriptor { - width: width as u32, - height: height as u32, - stride: None, - format: webrender_api::ImageFormat::BGRA8, - offset: 0, - is_opaque: false, - }; - let data = webrender_api::ImageData::Raw(Arc::new(pixels)); - - let mut updates = webrender_api::ResourceUpdates::new(); - - match *image_key { - Some(image_key) => { - updates.update_image(image_key, - descriptor, - data, - None); - } - None => { - *image_key = Some(webrender_api.generate_image_key()); - updates.add_image(image_key.unwrap(), - descriptor, - data, - None); - } - } - - if let Some(image_key) = mem::replace(very_old_image_key, old_image_key.take()) { - updates.delete_image(image_key); - } - - webrender_api.update_resources(updates); - - let image_data = CanvasImageData { - image_key: image_key.unwrap(), - }; - - chan.send(CanvasData::Image(image_data)).unwrap(); - } - WebGLPaintTaskData::WebRender(_, id) => { - chan.send(CanvasData::WebGL(id)).unwrap(); - } - } - } - - #[allow(unsafe_code)] - fn recreate(&mut self, size: Size2D<i32>) -> Result<(), &'static str> { - match self.data { - WebGLPaintTaskData::Readback { ref mut context, ref mut image_key, ref mut old_image_key, .. } => { - if size.width > self.size.width || - size.height > self.size.height { - self.size = context.resize(size)?; - } else { - self.size = size; - context.gl().scissor(0, 0, size.width, size.height); - } - // Webrender doesn't let images change size, so we clear the webrender image key. - if let Some(image_key) = image_key.take() { - // If this executes, then we are in a new epoch since we last recreated the canvas, - // so `old_image_key` must be `None`. - debug_assert!(old_image_key.is_none()); - *old_image_key = Some(image_key); - } - } - WebGLPaintTaskData::WebRender(ref api, id) => { - let device_size = webrender_api::DeviceIntSize::from_untyped(&size); - api.resize_webgl_context(id, &device_size); - } - } - - Ok(()) - } - - fn init(&mut self) { - if let WebGLPaintTaskData::Readback { ref context, .. } = self.data { - context.make_current(); - } - } -} - -impl Drop for WebGLPaintThread { - fn drop(&mut self) { - if let WebGLPaintTaskData::Readback { - ref mut webrender_api, - image_key, - old_image_key, - very_old_image_key, - .. - } = self.data { - let mut updates = webrender_api::ResourceUpdates::new(); - - if let Some(image_key) = image_key { - updates.delete_image(image_key); - } - if let Some(image_key) = old_image_key { - updates.delete_image(image_key); - } - if let Some(image_key) = very_old_image_key { - updates.delete_image(image_key); - } - - webrender_api.update_resources(updates); - } - } -} diff --git a/components/canvas/webgl_thread.rs b/components/canvas/webgl_thread.rs new file mode 100644 index 00000000000..907233a3b89 --- /dev/null +++ b/components/canvas/webgl_thread.rs @@ -0,0 +1,1204 @@ +/* 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_traits::canvas::byte_swap; +use canvas_traits::webgl::*; +use euclid::Size2D; +use gleam::gl; +use offscreen_gl_context::{GLContext, GLContextAttributes, GLLimits, NativeGLContextMethods}; +use std::collections::HashMap; +use std::mem; +use std::thread; +use super::gl_context::{GLContextFactory, GLContextWrapper}; +use webrender; +use webrender_api; + +/// WebGL Threading API entry point that lives in the constellation. +/// It allows to get a WebGLThread handle for each script pipeline. +pub use ::webgl_mode::WebGLThreads; + +/// A WebGLThread manages the life cycle and message multiplexing of +/// a set of WebGLContexts living in the same thread. +pub struct WebGLThread<VR: WebVRRenderHandler + 'static, OB: WebGLThreadObserver> { + /// Factory used to create a new GLContext shared with the WR/Main thread. + gl_factory: GLContextFactory, + /// Channel used to generate/update or delete `webrender_api::ImageKey`s. + webrender_api: webrender_api::RenderApi, + /// Map of live WebGLContexts. + contexts: HashMap<WebGLContextId, GLContextWrapper>, + /// Cached information for WebGLContexts. + cached_context_info: HashMap<WebGLContextId, WebGLContextInfo>, + /// Current bound context. + bound_context_id: Option<WebGLContextId>, + /// Id generator for new WebGLContexts. + next_webgl_id: usize, + /// Handler user to send WebVR commands. + webvr_compositor: Option<VR>, + /// Generic observer that listens WebGLContext creation, resize or removal events. + observer: OB, +} + +impl<VR: WebVRRenderHandler + 'static, OB: WebGLThreadObserver> WebGLThread<VR, OB> { + pub fn new(gl_factory: GLContextFactory, + webrender_api_sender: webrender_api::RenderApiSender, + webvr_compositor: Option<VR>, + observer: OB) -> Self { + WebGLThread { + gl_factory, + webrender_api: webrender_api_sender.create_api(), + contexts: HashMap::new(), + cached_context_info: HashMap::new(), + bound_context_id: None, + next_webgl_id: 0, + webvr_compositor, + observer: observer, + } + } + + /// Creates a new `WebGLThread` and returns a Sender to + /// communicate with it. + pub fn start(gl_factory: GLContextFactory, + webrender_api_sender: webrender_api::RenderApiSender, + webvr_compositor: Option<VR>, + observer: OB) + -> WebGLSender<WebGLMsg> { + let (sender, receiver) = webgl_channel::<WebGLMsg>().unwrap(); + let result = sender.clone(); + thread::Builder::new().name("WebGLThread".to_owned()).spawn(move || { + let mut renderer = WebGLThread::new(gl_factory, + webrender_api_sender, + webvr_compositor, + observer); + let webgl_chan = WebGLChan(sender); + loop { + let msg = receiver.recv().unwrap(); + let exit = renderer.handle_msg(msg, &webgl_chan); + if exit { + return; + } + } + }).expect("Thread spawning failed"); + + result + } + + /// Handles a generic WebGLMsg message + #[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); + result_sender.send(result.map(|(id, limits, share_mode)| + WebGLCreateContextResult { + sender: WebGLMsgSender::new(id, webgl_chan.clone()), + limits: limits, + share_mode: share_mode, + } + )).unwrap(); + }, + WebGLMsg::ResizeContext(ctx_id, size, sender) => { + self.resize_webgl_context(ctx_id, size, sender); + }, + WebGLMsg::RemoveContext(ctx_id) => { + self.remove_webgl_context(ctx_id); + }, + WebGLMsg::WebGLCommand(ctx_id, command) => { + self.handle_webgl_command(ctx_id, command); + }, + WebGLMsg::WebVRCommand(ctx_id, command) => { + self.handle_webvr_command(ctx_id, command); + }, + WebGLMsg::Lock(ctx_id, sender) => { + self.handle_lock(ctx_id, sender); + }, + WebGLMsg::Unlock(ctx_id) => { + self.handle_unlock(ctx_id); + }, + WebGLMsg::UpdateWebRenderImage(ctx_id, sender) => { + self.handle_update_wr_image(ctx_id, sender); + }, + WebGLMsg::Exit => { + return true; + } + } + + false + } + + /// Handles a WebGLCommand for a specific WebGLContext + fn handle_webgl_command(&mut self, context_id: WebGLContextId, command: WebGLCommand) { + if let Some(ctx) = Self::make_current_if_needed(context_id, &self.contexts, &mut self.bound_context_id) { + ctx.apply_command(command); + } + } + + /// Handles a WebVRCommand for a specific WebGLContext + fn handle_webvr_command(&mut self, context_id: WebGLContextId, command: WebVRCommand) { + Self::make_current_if_needed(context_id, &self.contexts, &mut self.bound_context_id); + let texture = match command { + WebVRCommand::SubmitFrame(..) => { + self.cached_context_info.get(&context_id) + }, + _ => None + }; + self.webvr_compositor.as_mut().unwrap().handle(command, texture.map(|t| (t.texture_id, t.size))); + } + + /// Handles a lock external callback received from webrender::ExternalImageHandler + fn handle_lock(&mut self, context_id: WebGLContextId, sender: WebGLSender<(u32, Size2D<i32>)>) { + let ctx = Self::make_current_if_needed(context_id, &self.contexts, &mut self.bound_context_id) + .expect("WebGLContext not found in a WebGLMsg::Lock message"); + let info = self.cached_context_info.get_mut(&context_id).unwrap(); + // Use a OpenGL Fence to perform the lock. + info.gl_sync = Some(ctx.gl().fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0)); + + sender.send((info.texture_id, info.size)).unwrap(); + } + + /// Handles an unlock external callback received from webrender::ExternalImageHandler + fn handle_unlock(&mut self, context_id: WebGLContextId) { + let ctx = Self::make_current_if_needed(context_id, &self.contexts, &mut self.bound_context_id) + .expect("WebGLContext not found in a WebGLMsg::Unlock message"); + let info = self.cached_context_info.get_mut(&context_id).unwrap(); + if let Some(gl_sync) = info.gl_sync.take() { + // glFlush must be called before glWaitSync. + ctx.gl().flush(); + // Wait until the GLSync object is signaled. + ctx.gl().wait_sync(gl_sync, 0, gl::TIMEOUT_IGNORED); + // Release the GLSync object. + ctx.gl().delete_sync(gl_sync); + } + } + + /// Creates a new WebGLContext + fn create_webgl_context(&mut self, + 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) + .map(|r| (r, WebGLContextShareMode::SharedTexture)) + .or_else(|_| { + let ctx = self.gl_factory.new_context(size, attributes); + ctx.map(|r| (r, WebGLContextShareMode::Readback)) + }); + + // Creating a new GLContext may make the current bound context_id dirty. + // Clear it to ensure that make_current() is called in subsequent commands. + self.bound_context_id = None; + + match result { + Ok((ctx, share_mode)) => { + let id = WebGLContextId(self.next_webgl_id); + let (size, texture_id, limits) = ctx.get_info(); + self.next_webgl_id += 1; + self.contexts.insert(id, ctx); + self.cached_context_info.insert(id, WebGLContextInfo { + texture_id, + size, + alpha: attributes.alpha, + image_key: None, + share_mode, + gl_sync: None, + old_image_key: None, + very_old_image_key: None, + }); + + self.observer.on_context_create(id, texture_id, size); + + Ok((id, limits, share_mode)) + }, + Err(msg) => { + Err(msg.to_owned()) + } + } + } + + /// Resizes a WebGLContext + fn resize_webgl_context(&mut self, + context_id: WebGLContextId, + size: Size2D<i32>, + sender: WebGLSender<Result<(), String>>) { + let ctx = Self::make_current_if_needed_mut(context_id, &mut self.contexts, &mut self.bound_context_id); + match ctx.resize(size) { + Ok(_) => { + let (real_size, texture_id, _) = ctx.get_info(); + self.observer.on_context_resize(context_id, texture_id, real_size); + + let info = self.cached_context_info.get_mut(&context_id).unwrap(); + // Update webgl texture size. Texture id may change too. + info.texture_id = texture_id; + info.size = real_size; + // WR doesn't support resizing and requires to create a new `ImageKey`. + // Mark the current image_key to be deleted later in the next epoch. + if let Some(image_key) = info.image_key.take() { + // If this executes, then we are in a new epoch since we last recreated the canvas, + // so `old_image_key` must be `None`. + debug_assert!(info.old_image_key.is_none()); + info.old_image_key = Some(image_key); + } + + sender.send(Ok(())).unwrap(); + }, + Err(msg) => { + sender.send(Err(msg.into())).unwrap(); + } + } + } + + /// Removes a WebGLContext and releases attached resources. + fn remove_webgl_context(&mut self, context_id: WebGLContextId) { + // Release webrender image keys. + if let Some(info) = self.cached_context_info.remove(&context_id) { + let mut updates = webrender_api::ResourceUpdates::new(); + + if let Some(image_key) = info.image_key { + updates.delete_image(image_key); + } + if let Some(image_key) = info.old_image_key { + updates.delete_image(image_key); + } + if let Some(image_key) = info.very_old_image_key { + updates.delete_image(image_key); + } + + self.webrender_api.update_resources(updates) + } + + // Release GL context. + if self.contexts.remove(&context_id).is_some() { + self.observer.on_context_delete(context_id); + } + + // Removing a GLContext may make the current bound context_id dirty. + self.bound_context_id = None; + } + + /// Handles the creation/update of webrender_api::ImageKeys fpr a specific WebGLContext. + /// This method is invoked from a UpdateWebRenderImage message sent by the layout thread. + /// If SharedTexture is used the UpdateWebRenderImage message is sent only after a WebGLContext creation or resize. + /// If Readback is used UpdateWebRenderImage message is sent always on each layout iteration in order to + /// submit the updated raw pixels. + fn handle_update_wr_image(&mut self, context_id: WebGLContextId, sender: WebGLSender<webrender_api::ImageKey>) { + let info = self.cached_context_info.get_mut(&context_id).unwrap(); + let webrender_api = &self.webrender_api; + + let image_key = match info.share_mode { + WebGLContextShareMode::SharedTexture => { + let size = info.size; + let alpha = info.alpha; + // Reuse existing ImageKey or generate a new one. + // When using a shared texture ImageKeys are only generated after a WebGLContext creation or resize. + *info.image_key.get_or_insert_with(|| { + Self::create_wr_external_image(webrender_api, size, alpha, context_id) + }) + }, + WebGLContextShareMode::Readback => { + let pixels = Self::raw_pixels(&self.contexts[&context_id], info.size); + match info.image_key.clone() { + Some(image_key) => { + // ImageKey was already created, but WR Images must + // be updated every frame in readback mode to send the new raw pixels. + Self::update_wr_readback_image(webrender_api, + info.size, + info.alpha, + image_key, + pixels); + + image_key + }, + None => { + // Generate a new ImageKey for Readback mode. + let image_key = Self::create_wr_readback_image(webrender_api, + info.size, + info.alpha, + pixels); + info.image_key = Some(image_key); + image_key + } + } + } + }; + + // Delete old image + if let Some(image_key) = mem::replace(&mut info.very_old_image_key, info.old_image_key.take()) { + let mut updates = webrender_api::ResourceUpdates::new(); + updates.delete_image(image_key); + self.webrender_api.update_resources(updates); + } + + // Send the ImageKey to the Layout thread. + sender.send(image_key).unwrap(); + } + + /// Gets a reference to a GLContextWrapper for a given WebGLContextId and makes it current if required. + fn make_current_if_needed<'a>(context_id: WebGLContextId, + contexts: &'a HashMap<WebGLContextId, GLContextWrapper>, + bound_id: &mut Option<WebGLContextId>) -> Option<&'a GLContextWrapper> { + contexts.get(&context_id).and_then(|ctx| { + if Some(context_id) != *bound_id { + ctx.make_current(); + *bound_id = Some(context_id); + } + + Some(ctx) + }) + } + + /// Gets a mutable reference to a GLContextWrapper for a WebGLContextId and makes it current if required. + fn make_current_if_needed_mut<'a>(context_id: WebGLContextId, + contexts: &'a mut HashMap<WebGLContextId, GLContextWrapper>, + bound_id: &mut Option<WebGLContextId>) -> &'a mut GLContextWrapper { + let ctx = contexts.get_mut(&context_id).expect("WebGLContext not found!"); + if Some(context_id) != *bound_id { + ctx.make_current(); + *bound_id = Some(context_id); + } + ctx + } + + /// Creates a `webrender_api::ImageKey` that uses shared textures. + fn create_wr_external_image(webrender_api: &webrender_api::RenderApi, + size: Size2D<i32>, + alpha: bool, + context_id: WebGLContextId) -> webrender_api::ImageKey { + let descriptor = Self::image_descriptor(size, alpha); + + let data = webrender_api::ExternalImageData { + id: webrender_api::ExternalImageId(context_id.0 as u64), + channel_index: 0, + image_type: webrender_api::ExternalImageType::Texture2DHandle, + }; + let data = webrender_api::ImageData::External(data); + + let image_key = webrender_api.generate_image_key(); + let mut updates = webrender_api::ResourceUpdates::new(); + updates.add_image(image_key, + descriptor, + data, + None); + webrender_api.update_resources(updates); + + image_key + } + + /// Creates a `webrender_api::ImageKey` that uses raw pixels. + fn create_wr_readback_image(webrender_api: &webrender_api::RenderApi, + size: Size2D<i32>, + alpha: bool, + data: Vec<u8>) -> webrender_api::ImageKey { + let descriptor = Self::image_descriptor(size, alpha); + let data = webrender_api::ImageData::new(data); + + let image_key = webrender_api.generate_image_key(); + let mut updates = webrender_api::ResourceUpdates::new(); + updates.add_image(image_key, + descriptor, + data, + None); + webrender_api.update_resources(updates); + + image_key + } + + /// Updates a `webrender_api::ImageKey` that uses raw pixels. + fn update_wr_readback_image(webrender_api: &webrender_api::RenderApi, + size: Size2D<i32>, + alpha: bool, + image_key: webrender_api::ImageKey, + data: Vec<u8>) { + let descriptor = Self::image_descriptor(size, alpha); + let data = webrender_api::ImageData::new(data); + + let mut updates = webrender_api::ResourceUpdates::new(); + updates.update_image(image_key, + descriptor, + data, + None); + webrender_api.update_resources(updates); + } + + /// Helper function to create a `webrender_api::ImageDescriptor`. + fn image_descriptor(size: Size2D<i32>, alpha: bool) -> webrender_api::ImageDescriptor { + webrender_api::ImageDescriptor { + width: size.width as u32, + height: size.height as u32, + stride: None, + format: if alpha { webrender_api::ImageFormat::BGRA8 } else { webrender_api::ImageFormat::RGB8 }, + offset: 0, + is_opaque: !alpha, + } + } + + /// Helper function to fetch the raw pixels used in readback mode. + fn raw_pixels(context: &GLContextWrapper, size: Size2D<i32>) -> Vec<u8> { + let width = size.width as usize; + let height = size.height as usize; + + let mut pixels = context.gl().read_pixels(0, 0, + size.width as gl::GLsizei, + 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]); + } + byte_swap(&mut pixels); + pixels + } +} + +impl<VR: WebVRRenderHandler + 'static, OB: WebGLThreadObserver> Drop for WebGLThread<VR, OB> { + fn drop(&mut self) { + // Call remove_context functions in order to correctly delete WebRender image keys. + let context_ids: Vec<WebGLContextId> = self.contexts.keys().map(|id| *id).collect(); + for id in context_ids { + self.remove_webgl_context(id); + } + } +} + +/// Helper struct to store cached WebGLContext information. +struct WebGLContextInfo { + /// Render to texture identifier used by the WebGLContext. + texture_id: u32, + /// Size of the WebGLContext. + size: Size2D<i32>, + /// True if the WebGLContext uses an alpha channel. + alpha: bool, + /// Currently used WebRender image key. + image_key: Option<webrender_api::ImageKey>, + /// The sharing mode used to send the image to WebRender. + share_mode: WebGLContextShareMode, + /// GLSync Object used for a correct synchronization with Webrender external image callbacks. + gl_sync: Option<gl::GLsync>, + /// An old WebRender image key that can be deleted when the next epoch ends. + old_image_key: Option<webrender_api::ImageKey>, + /// An old WebRender image key that can be deleted when the current epoch ends. + very_old_image_key: Option<webrender_api::ImageKey>, +} + +/// Trait used to observe events in a WebGL Thread. +/// Used in webrender::ExternalImageHandler when multiple WebGL threads are used. +pub trait WebGLThreadObserver: Send + 'static { + fn on_context_create(&mut self, ctx_id: WebGLContextId, texture_id: u32, size: Size2D<i32>); + fn on_context_resize(&mut self, ctx_id: WebGLContextId, texture_id: u32, size: Size2D<i32>); + fn on_context_delete(&mut self, ctx_id: WebGLContextId); +} + +/// This trait is used as a bridge between the `WebGLThreads` implementation and +/// the WR ExternalImageHandler API implemented in the `WebGLExternalImageHandler` struct. +/// `WebGLExternalImageHandler<T>` takes care of type conversions between WR and WebGL info (e.g keys, uvs). +/// It uses this trait to notify lock/unlock messages and get the required info that WR needs. +/// `WebGLThreads` receives lock/unlock message notifications and takes care of sending +/// the unlock/lock messages to the appropiate `WebGLThread`. +pub trait WebGLExternalImageApi { + fn lock(&mut self, ctx_id: WebGLContextId) -> (u32, Size2D<i32>); + fn unlock(&mut self, ctx_id: WebGLContextId); +} + +/// WebRender External Image Handler implementation +pub struct WebGLExternalImageHandler<T: WebGLExternalImageApi> { + handler: T, +} + +impl<T: WebGLExternalImageApi> WebGLExternalImageHandler<T> { + pub fn new(handler: T) -> Self { + Self { + handler: handler + } + } +} + +impl<T: WebGLExternalImageApi> webrender::ExternalImageHandler for WebGLExternalImageHandler<T> { + /// Lock the external image. Then, WR could start to read the image content. + /// The WR client should not change the image content until the unlock() call. + fn lock(&mut self, + key: webrender_api::ExternalImageId, + _channel_index: u8) -> webrender::ExternalImage { + let ctx_id = WebGLContextId(key.0 as _); + let (texture_id, size) = self.handler.lock(ctx_id); + + webrender::ExternalImage { + u0: 0.0, + u1: size.width as f32, + v1: 0.0, + v0: size.height as f32, + source: webrender::ExternalImageSource::NativeTexture(texture_id), + } + + } + /// Unlock the external image. The WR should not read the image content + /// after this call. + fn unlock(&mut self, + key: webrender_api::ExternalImageId, + _channel_index: u8) { + let ctx_id = WebGLContextId(key.0 as _); + self.handler.unlock(ctx_id); + } +} + +/// WebGL Commands Implementation +pub struct WebGLImpl; + +impl WebGLImpl { + pub fn apply<Native: NativeGLContextMethods>(ctx: &GLContext<Native>, command: WebGLCommand) { + match command { + WebGLCommand::GetContextAttributes(sender) => + sender.send(*ctx.borrow_attributes()).unwrap(), + WebGLCommand::ActiveTexture(target) => + ctx.gl().active_texture(target), + WebGLCommand::AttachShader(program_id, shader_id) => + ctx.gl().attach_shader(program_id.get(), shader_id.get()), + WebGLCommand::DetachShader(program_id, shader_id) => + ctx.gl().detach_shader(program_id.get(), shader_id.get()), + WebGLCommand::BindAttribLocation(program_id, index, name) => + ctx.gl().bind_attrib_location(program_id.get(), index, &name), + WebGLCommand::BlendColor(r, g, b, a) => + ctx.gl().blend_color(r, g, b, a), + WebGLCommand::BlendEquation(mode) => + ctx.gl().blend_equation(mode), + WebGLCommand::BlendEquationSeparate(mode_rgb, mode_alpha) => + ctx.gl().blend_equation_separate(mode_rgb, mode_alpha), + WebGLCommand::BlendFunc(src, dest) => + ctx.gl().blend_func(src, dest), + WebGLCommand::BlendFuncSeparate(src_rgb, dest_rgb, src_alpha, dest_alpha) => + ctx.gl().blend_func_separate(src_rgb, dest_rgb, src_alpha, dest_alpha), + WebGLCommand::BufferData(buffer_type, data, usage) => + gl::buffer_data(ctx.gl(), buffer_type, &data, usage), + WebGLCommand::BufferSubData(buffer_type, offset, data) => + gl::buffer_sub_data(ctx.gl(), buffer_type, offset, &data), + WebGLCommand::Clear(mask) => + ctx.gl().clear(mask), + WebGLCommand::ClearColor(r, g, b, a) => + ctx.gl().clear_color(r, g, b, a), + WebGLCommand::ClearDepth(depth) => + ctx.gl().clear_depth(depth), + WebGLCommand::ClearStencil(stencil) => + ctx.gl().clear_stencil(stencil), + WebGLCommand::ColorMask(r, g, b, a) => + ctx.gl().color_mask(r, g, b, a), + WebGLCommand::CopyTexImage2D(target, level, internal_format, x, y, width, height, border) => + ctx.gl().copy_tex_image_2d(target, level, internal_format, x, y, width, height, border), + WebGLCommand::CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height) => + ctx.gl().copy_tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height), + WebGLCommand::CullFace(mode) => + ctx.gl().cull_face(mode), + WebGLCommand::DepthFunc(func) => + ctx.gl().depth_func(func), + WebGLCommand::DepthMask(flag) => + ctx.gl().depth_mask(flag), + WebGLCommand::DepthRange(near, far) => + ctx.gl().depth_range(near, far), + WebGLCommand::Disable(cap) => + ctx.gl().disable(cap), + WebGLCommand::Enable(cap) => + ctx.gl().enable(cap), + WebGLCommand::FramebufferRenderbuffer(target, attachment, renderbuffertarget, rb) => + ctx.gl().framebuffer_renderbuffer(target, attachment, renderbuffertarget, + rb.map_or(0, WebGLRenderbufferId::get)), + WebGLCommand::FramebufferTexture2D(target, attachment, textarget, texture, level) => + ctx.gl().framebuffer_texture_2d(target, attachment, textarget, + texture.map_or(0, WebGLTextureId::get), level), + WebGLCommand::FrontFace(mode) => + ctx.gl().front_face(mode), + WebGLCommand::DisableVertexAttribArray(attrib_id) => + ctx.gl().disable_vertex_attrib_array(attrib_id), + WebGLCommand::DrawArrays(mode, first, count) => + ctx.gl().draw_arrays(mode, first, count), + WebGLCommand::DrawElements(mode, count, type_, offset) => + ctx.gl().draw_elements(mode, count, type_, offset as u32), + WebGLCommand::EnableVertexAttribArray(attrib_id) => + ctx.gl().enable_vertex_attrib_array(attrib_id), + WebGLCommand::Hint(name, val) => + ctx.gl().hint(name, val), + WebGLCommand::IsEnabled(cap, chan) => + chan.send(ctx.gl().is_enabled(cap) != 0).unwrap(), + WebGLCommand::LineWidth(width) => + ctx.gl().line_width(width), + WebGLCommand::PixelStorei(name, val) => + ctx.gl().pixel_store_i(name, val), + WebGLCommand::PolygonOffset(factor, units) => + ctx.gl().polygon_offset(factor, units), + WebGLCommand::ReadPixels(x, y, width, height, format, pixel_type, chan) => + Self::read_pixels(ctx.gl(), x, y, width, height, format, pixel_type, chan), + WebGLCommand::RenderbufferStorage(target, format, width, height) => + ctx.gl().renderbuffer_storage(target, format, width, height), + WebGLCommand::SampleCoverage(value, invert) => + ctx.gl().sample_coverage(value, invert), + WebGLCommand::Scissor(x, y, width, height) => + ctx.gl().scissor(x, y, width, height), + WebGLCommand::StencilFunc(func, ref_, mask) => + ctx.gl().stencil_func(func, ref_, mask), + WebGLCommand::StencilFuncSeparate(face, func, ref_, mask) => + ctx.gl().stencil_func_separate(face, func, ref_, mask), + WebGLCommand::StencilMask(mask) => + ctx.gl().stencil_mask(mask), + WebGLCommand::StencilMaskSeparate(face, mask) => + ctx.gl().stencil_mask_separate(face, mask), + WebGLCommand::StencilOp(fail, zfail, zpass) => + ctx.gl().stencil_op(fail, zfail, zpass), + WebGLCommand::StencilOpSeparate(face, fail, zfail, zpass) => + ctx.gl().stencil_op_separate(face, fail, zfail, zpass), + WebGLCommand::GetActiveAttrib(program_id, index, chan) => + Self::active_attrib(ctx.gl(), program_id, index, chan), + WebGLCommand::GetActiveUniform(program_id, index, chan) => + Self::active_uniform(ctx.gl(), program_id, index, chan), + WebGLCommand::GetAttribLocation(program_id, name, chan) => + Self::attrib_location(ctx.gl(), program_id, name, chan), + WebGLCommand::GetVertexAttrib(index, pname, chan) => + Self::vertex_attrib(ctx.gl(), index, pname, chan), + WebGLCommand::GetVertexAttribOffset(index, pname, chan) => + Self::vertex_attrib_offset(ctx.gl(), index, pname, chan), + WebGLCommand::GetBufferParameter(target, param_id, chan) => + Self::buffer_parameter(ctx.gl(), target, param_id, chan), + WebGLCommand::GetParameter(param_id, chan) => + Self::parameter(ctx.gl(), param_id, chan), + WebGLCommand::GetProgramParameter(program_id, param_id, chan) => + Self::program_parameter(ctx.gl(), program_id, param_id, chan), + WebGLCommand::GetShaderParameter(shader_id, param_id, chan) => + Self::shader_parameter(ctx.gl(), shader_id, param_id, chan), + WebGLCommand::GetShaderPrecisionFormat(shader_type, precision_type, chan) => + Self::shader_precision_format(ctx.gl(), shader_type, precision_type, chan), + WebGLCommand::GetExtensions(chan) => + Self::get_extensions(ctx.gl(), chan), + WebGLCommand::GetUniformLocation(program_id, name, chan) => + Self::uniform_location(ctx.gl(), program_id, name, chan), + WebGLCommand::GetShaderInfoLog(shader_id, chan) => + Self::shader_info_log(ctx.gl(), shader_id, chan), + WebGLCommand::GetProgramInfoLog(program_id, chan) => + Self::program_info_log(ctx.gl(), program_id, chan), + WebGLCommand::CompileShader(shader_id, source) => + Self::compile_shader(ctx.gl(), shader_id, source), + WebGLCommand::CreateBuffer(chan) => + Self::create_buffer(ctx.gl(), chan), + WebGLCommand::CreateFramebuffer(chan) => + Self::create_framebuffer(ctx.gl(), chan), + WebGLCommand::CreateRenderbuffer(chan) => + Self::create_renderbuffer(ctx.gl(), chan), + WebGLCommand::CreateTexture(chan) => + Self::create_texture(ctx.gl(), chan), + WebGLCommand::CreateProgram(chan) => + Self::create_program(ctx.gl(), chan), + WebGLCommand::CreateShader(shader_type, chan) => + Self::create_shader(ctx.gl(), shader_type, chan), + WebGLCommand::DeleteBuffer(id) => + ctx.gl().delete_buffers(&[id.get()]), + WebGLCommand::DeleteFramebuffer(id) => + ctx.gl().delete_framebuffers(&[id.get()]), + WebGLCommand::DeleteRenderbuffer(id) => + ctx.gl().delete_renderbuffers(&[id.get()]), + WebGLCommand::DeleteTexture(id) => + ctx.gl().delete_textures(&[id.get()]), + WebGLCommand::DeleteProgram(id) => + ctx.gl().delete_program(id.get()), + WebGLCommand::DeleteShader(id) => + ctx.gl().delete_shader(id.get()), + WebGLCommand::BindBuffer(target, id) => + ctx.gl().bind_buffer(target, id.map_or(0, WebGLBufferId::get)), + WebGLCommand::BindFramebuffer(target, request) => + Self::bind_framebuffer(ctx.gl(), target, request, ctx), + WebGLCommand::BindRenderbuffer(target, id) => + ctx.gl().bind_renderbuffer(target, id.map_or(0, WebGLRenderbufferId::get)), + WebGLCommand::BindTexture(target, id) => + ctx.gl().bind_texture(target, id.map_or(0, WebGLTextureId::get)), + WebGLCommand::LinkProgram(program_id) => + ctx.gl().link_program(program_id.get()), + WebGLCommand::Uniform1f(uniform_id, v) => + ctx.gl().uniform_1f(uniform_id, v), + WebGLCommand::Uniform1fv(uniform_id, v) => + ctx.gl().uniform_1fv(uniform_id, &v), + WebGLCommand::Uniform1i(uniform_id, v) => + ctx.gl().uniform_1i(uniform_id, v), + WebGLCommand::Uniform1iv(uniform_id, v) => + ctx.gl().uniform_1iv(uniform_id, &v), + WebGLCommand::Uniform2f(uniform_id, x, y) => + ctx.gl().uniform_2f(uniform_id, x, y), + WebGLCommand::Uniform2fv(uniform_id, v) => + ctx.gl().uniform_2fv(uniform_id, &v), + WebGLCommand::Uniform2i(uniform_id, x, y) => + ctx.gl().uniform_2i(uniform_id, x, y), + WebGLCommand::Uniform2iv(uniform_id, v) => + ctx.gl().uniform_2iv(uniform_id, &v), + WebGLCommand::Uniform3f(uniform_id, x, y, z) => + ctx.gl().uniform_3f(uniform_id, x, y, z), + WebGLCommand::Uniform3fv(uniform_id, v) => + ctx.gl().uniform_3fv(uniform_id, &v), + WebGLCommand::Uniform3i(uniform_id, x, y, z) => + ctx.gl().uniform_3i(uniform_id, x, y, z), + WebGLCommand::Uniform3iv(uniform_id, v) => + ctx.gl().uniform_3iv(uniform_id, &v), + WebGLCommand::Uniform4f(uniform_id, x, y, z, w) => + ctx.gl().uniform_4f(uniform_id, x, y, z, w), + WebGLCommand::Uniform4fv(uniform_id, v) => + ctx.gl().uniform_4fv(uniform_id, &v), + WebGLCommand::Uniform4i(uniform_id, x, y, z, w) => + ctx.gl().uniform_4i(uniform_id, x, y, z, w), + WebGLCommand::Uniform4iv(uniform_id, v) => + ctx.gl().uniform_4iv(uniform_id, &v), + WebGLCommand::UniformMatrix2fv(uniform_id, transpose, v) => + ctx.gl().uniform_matrix_2fv(uniform_id, transpose, &v), + WebGLCommand::UniformMatrix3fv(uniform_id, transpose, v) => + ctx.gl().uniform_matrix_3fv(uniform_id, transpose, &v), + WebGLCommand::UniformMatrix4fv(uniform_id, transpose, v) => + ctx.gl().uniform_matrix_4fv(uniform_id, transpose, &v), + WebGLCommand::UseProgram(program_id) => + ctx.gl().use_program(program_id.get()), + WebGLCommand::ValidateProgram(program_id) => + ctx.gl().validate_program(program_id.get()), + WebGLCommand::VertexAttrib(attrib_id, x, y, z, w) => + ctx.gl().vertex_attrib_4f(attrib_id, x, y, z, w), + WebGLCommand::VertexAttribPointer2f(attrib_id, size, normalized, stride, offset) => + ctx.gl().vertex_attrib_pointer_f32(attrib_id, size, normalized, stride, offset), + WebGLCommand::VertexAttribPointer(attrib_id, size, data_type, normalized, stride, offset) => + ctx.gl().vertex_attrib_pointer(attrib_id, size, data_type, normalized, stride, offset), + WebGLCommand::Viewport(x, y, width, height) => + ctx.gl().viewport(x, y, width, height), + WebGLCommand::TexImage2D(target, level, internal, width, height, format, data_type, data) => + ctx.gl().tex_image_2d(target, level, internal, width, height, + /*border*/0, format, data_type, Some(&data)), + WebGLCommand::TexParameteri(target, name, value) => + ctx.gl().tex_parameter_i(target, name, value), + WebGLCommand::TexParameterf(target, name, value) => + ctx.gl().tex_parameter_f(target, name, value), + WebGLCommand::TexSubImage2D(target, level, xoffset, yoffset, x, y, width, height, data) => + ctx.gl().tex_sub_image_2d(target, level, xoffset, yoffset, x, y, width, height, &data), + WebGLCommand::DrawingBufferWidth(sender) => + sender.send(ctx.borrow_draw_buffer().unwrap().size().width).unwrap(), + WebGLCommand::DrawingBufferHeight(sender) => + sender.send(ctx.borrow_draw_buffer().unwrap().size().height).unwrap(), + WebGLCommand::Finish(sender) => + Self::finish(ctx.gl(), sender), + WebGLCommand::Flush => + ctx.gl().flush(), + WebGLCommand::GenerateMipmap(target) => + ctx.gl().generate_mipmap(target), + WebGLCommand::CreateVertexArray(chan) => + Self::create_vertex_array(ctx.gl(), chan), + WebGLCommand::DeleteVertexArray(id) => + ctx.gl().delete_vertex_arrays(&[id.get()]), + WebGLCommand::BindVertexArray(id) => + ctx.gl().bind_vertex_array(id.map_or(0, WebGLVertexArrayId::get)), + } + + // TODO: update test expectations in order to enable debug assertions + //if cfg!(debug_assertions) { + let error = ctx.gl().get_error(); + assert!(error == gl::NO_ERROR, "Unexpected WebGL error: 0x{:x} ({})", error, error); + //} + } + + fn read_pixels(gl: &gl::Gl, x: i32, y: i32, width: i32, height: i32, format: u32, pixel_type: u32, + chan: WebGLSender<Vec<u8>>) { + let result = gl.read_pixels(x, y, width, height, format, pixel_type); + chan.send(result).unwrap() + } + + fn active_attrib(gl: &gl::Gl, + program_id: WebGLProgramId, + index: u32, + chan: WebGLSender<WebGLResult<(i32, u32, String)>>) { + let result = if index >= gl.get_program_iv(program_id.get(), gl::ACTIVE_ATTRIBUTES) as u32 { + Err(WebGLError::InvalidValue) + } else { + Ok(gl.get_active_attrib(program_id.get(), index)) + }; + chan.send(result).unwrap(); + } + + fn active_uniform(gl: &gl::Gl, + program_id: WebGLProgramId, + index: u32, + chan: WebGLSender<WebGLResult<(i32, u32, String)>>) { + let result = if index >= gl.get_program_iv(program_id.get(), gl::ACTIVE_UNIFORMS) as u32 { + Err(WebGLError::InvalidValue) + } else { + Ok(gl.get_active_uniform(program_id.get(), index)) + }; + chan.send(result).unwrap(); + } + + fn attrib_location(gl: &gl::Gl, + program_id: WebGLProgramId, + name: String, + chan: WebGLSender<Option<i32>> ) { + let attrib_location = gl.get_attrib_location(program_id.get(), &name); + + let attrib_location = if attrib_location == -1 { + None + } else { + Some(attrib_location) + }; + + chan.send(attrib_location).unwrap(); + } + + fn parameter(gl: &gl::Gl, + param_id: u32, + chan: WebGLSender<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, emilio): 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 finish(gl: &gl::Gl, chan: WebGLSender<()>) { + gl.finish(); + chan.send(()).unwrap(); + } + + fn vertex_attrib(gl: &gl::Gl, + index: u32, + pname: u32, + chan: WebGLSender<WebGLResult<WebGLParameter>>) { + let result = if index >= gl.get_integer_v(gl::MAX_VERTEX_ATTRIBS) as u32 { + Err(WebGLError::InvalidValue) + } else { + match pname { + gl::VERTEX_ATTRIB_ARRAY_ENABLED | + gl::VERTEX_ATTRIB_ARRAY_NORMALIZED => + Ok(WebGLParameter::Bool(gl.get_vertex_attrib_iv(index, pname) != 0)), + gl::VERTEX_ATTRIB_ARRAY_SIZE | + gl::VERTEX_ATTRIB_ARRAY_STRIDE | + gl::VERTEX_ATTRIB_ARRAY_TYPE => + Ok(WebGLParameter::Int(gl.get_vertex_attrib_iv(index, pname))), + gl::CURRENT_VERTEX_ATTRIB => + Ok(WebGLParameter::FloatArray(gl.get_vertex_attrib_fv(index, pname))), + // gl::VERTEX_ATTRIB_ARRAY_BUFFER_BINDING should return WebGLBuffer + _ => Err(WebGLError::InvalidEnum), + } + }; + + chan.send(result).unwrap(); + } + + fn vertex_attrib_offset(gl: &gl::Gl, + index: u32, + pname: u32, + chan: WebGLSender<WebGLResult<isize>>) { + let result = match pname { + gl::VERTEX_ATTRIB_ARRAY_POINTER => Ok(gl.get_vertex_attrib_pointer_v(index, pname)), + _ => Err(WebGLError::InvalidEnum), + }; + + chan.send(result).unwrap(); + } + + fn buffer_parameter(gl: &gl::Gl, + target: u32, + param_id: u32, + chan: WebGLSender<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(gl: &gl::Gl, + program_id: WebGLProgramId, + param_id: u32, + chan: WebGLSender<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.get(), param_id) != 0)), + gl::ATTACHED_SHADERS | + gl::ACTIVE_ATTRIBUTES | + gl::ACTIVE_UNIFORMS => + Ok(WebGLParameter::Int(gl.get_program_iv(program_id.get(), param_id))), + _ => Err(WebGLError::InvalidEnum), + }; + + chan.send(result).unwrap(); + } + + fn shader_parameter(gl: &gl::Gl, + shader_id: WebGLShaderId, + param_id: u32, + chan: WebGLSender<WebGLResult<WebGLParameter>>) { + let result = match param_id { + gl::SHADER_TYPE => + Ok(WebGLParameter::Int(gl.get_shader_iv(shader_id.get(), param_id))), + gl::DELETE_STATUS | + gl::COMPILE_STATUS => + Ok(WebGLParameter::Bool(gl.get_shader_iv(shader_id.get(), param_id) != 0)), + _ => Err(WebGLError::InvalidEnum), + }; + + chan.send(result).unwrap(); + } + + fn shader_precision_format(gl: &gl::Gl, + shader_type: u32, + precision_type: u32, + chan: WebGLSender<WebGLResult<(i32, i32, i32)>>) { + let result = match precision_type { + gl::LOW_FLOAT | + gl::MEDIUM_FLOAT | + gl::HIGH_FLOAT | + gl::LOW_INT | + gl::MEDIUM_INT | + gl::HIGH_INT => { + Ok(gl.get_shader_precision_format(shader_type, precision_type)) + }, + _=> { + Err(WebGLError::InvalidEnum) + } + }; + + chan.send(result).unwrap(); + } + + fn get_extensions(gl: &gl::Gl, chan: WebGLSender<String>) { + chan.send(gl.get_string(gl::EXTENSIONS)).unwrap(); + } + + fn uniform_location(gl: &gl::Gl, + program_id: WebGLProgramId, + name: String, + chan: WebGLSender<Option<i32>>) { + let location = gl.get_uniform_location(program_id.get(), &name); + let location = if location == -1 { + None + } else { + Some(location) + }; + + chan.send(location).unwrap(); + } + + + fn shader_info_log(gl: &gl::Gl, shader_id: WebGLShaderId, chan: WebGLSender<String>) { + let log = gl.get_shader_info_log(shader_id.get()); + chan.send(log).unwrap(); + } + + fn program_info_log(gl: &gl::Gl, program_id: WebGLProgramId, chan: WebGLSender<String>) { + let log = gl.get_program_info_log(program_id.get()); + chan.send(log).unwrap(); + } + + #[allow(unsafe_code)] + fn create_buffer(gl: &gl::Gl, chan: WebGLSender<Option<WebGLBufferId>>) { + let buffer = gl.gen_buffers(1)[0]; + let buffer = if buffer == 0 { + None + } else { + Some(unsafe { WebGLBufferId::new(buffer) }) + }; + chan.send(buffer).unwrap(); + } + + #[allow(unsafe_code)] + fn create_framebuffer(gl: &gl::Gl, chan: WebGLSender<Option<WebGLFramebufferId>>) { + let framebuffer = gl.gen_framebuffers(1)[0]; + let framebuffer = if framebuffer == 0 { + None + } else { + Some(unsafe { WebGLFramebufferId::new(framebuffer) }) + }; + chan.send(framebuffer).unwrap(); + } + + #[allow(unsafe_code)] + fn create_renderbuffer(gl: &gl::Gl, chan: WebGLSender<Option<WebGLRenderbufferId>>) { + let renderbuffer = gl.gen_renderbuffers(1)[0]; + let renderbuffer = if renderbuffer == 0 { + None + } else { + Some(unsafe { WebGLRenderbufferId::new(renderbuffer) }) + }; + chan.send(renderbuffer).unwrap(); + } + + #[allow(unsafe_code)] + fn create_texture(gl: &gl::Gl, chan: WebGLSender<Option<WebGLTextureId>>) { + let texture = gl.gen_textures(1)[0]; + let texture = if texture == 0 { + None + } else { + Some(unsafe { WebGLTextureId::new(texture) }) + }; + chan.send(texture).unwrap(); + } + + #[allow(unsafe_code)] + fn create_program(gl: &gl::Gl, chan: WebGLSender<Option<WebGLProgramId>>) { + let program = gl.create_program(); + let program = if program == 0 { + None + } else { + Some(unsafe { WebGLProgramId::new(program) }) + }; + chan.send(program).unwrap(); + } + + #[allow(unsafe_code)] + fn create_shader(gl: &gl::Gl, shader_type: u32, chan: WebGLSender<Option<WebGLShaderId>>) { + let shader = gl.create_shader(shader_type); + let shader = if shader == 0 { + None + } else { + Some(unsafe { WebGLShaderId::new(shader) }) + }; + chan.send(shader).unwrap(); + } + + #[allow(unsafe_code)] + fn create_vertex_array(gl: &gl::Gl, chan: WebGLSender<Option<WebGLVertexArrayId>>) { + let vao = gl.gen_vertex_arrays(1)[0]; + let vao = if vao == 0 { + None + } else { + Some(unsafe { WebGLVertexArrayId::new(vao) }) + }; + chan.send(vao).unwrap(); + } + + #[inline] + fn bind_framebuffer<Native: NativeGLContextMethods>(gl: &gl::Gl, + target: u32, + request: WebGLFramebufferBindingRequest, + ctx: &GLContext<Native>) { + let id = match request { + WebGLFramebufferBindingRequest::Explicit(id) => id.get(), + WebGLFramebufferBindingRequest::Default => + ctx.borrow_draw_buffer().unwrap().get_framebuffer(), + }; + + gl.bind_framebuffer(target, id); + } + + + #[inline] + fn compile_shader(gl: &gl::Gl, shader_id: WebGLShaderId, source: String) { + gl.shader_source(shader_id.get(), &[source.as_bytes()]); + gl.compile_shader(shader_id.get()); + } +} |