diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2017-08-16 09:45:13 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-16 09:45:13 -0500 |
commit | 4d10d39e8fe841c5fe2ac58da2daaa13c10c140e (patch) | |
tree | f77d68b734a6327898cc8c01505b0723bf45ed4a /components/canvas | |
parent | ee94e2b7c0bd327abe8f9545b2a1f792f67a2bdd (diff) | |
parent | cfe22e3979b7270833a4b450b25fb2157deb1da2 (diff) | |
download | servo-4d10d39e8fe841c5fe2ac58da2daaa13c10c140e.tar.gz servo-4d10d39e8fe841c5fe2ac58da2daaa13c10c140e.zip |
Auto merge of #18114 - emilio:revert-webgl-refactor, r=nox
Revert "Auto merge of #17891 - MortimerGoro:webgl_move, r=glennw,emilio"
This reverts commit 90f55ea4580e2a15f7d70d0491444f18b972d450, reversing
changes made to 2e60b27a2186a8cba4b952960155dfcf3f47d7db.
Doing that per Josh's request, since it's causing very frequent intermittent OOMs on the android builders.
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/18114)
<!-- Reviewable:end -->
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, 395 insertions, 1521 deletions
diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index e8bf234a052..ad149daccb6 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -12,7 +12,6 @@ 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" @@ -20,5 +19,5 @@ ipc-channel = "0.8" log = "0.3.5" num-traits = "0.1.32" offscreen_gl_context = { version = "0.11", features = ["serde"] } -webrender = {git = "https://github.com/servo/webrender"} +servo_config = {path = "../config"} 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 8fe05d1f6a9..b7e21777001 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::canvas::*; +use canvas_traits::*; use cssparser::RGBA; use euclid::{Transform2D, Point2D, Vector2D, Rect, Size2D}; use ipc_channel::ipc::{self, IpcSender}; @@ -193,8 +193,12 @@ impl<'a> CanvasPaintThread<'a> { Canvas2dMsg::SetShadowColor(ref color) => painter.set_shadow_color(color.to_azure_style()), } }, - CanvasMsg::Close => break, - CanvasMsg::Recreate(size) => painter.recreate(size), + CanvasMsg::Common(message) => { + match message { + CanvasCommonMsg::Close => break, + CanvasCommonMsg::Recreate(size) => painter.recreate(size), + } + }, CanvasMsg::FromScript(message) => { match message { FromScriptMsg::SendPixels(chan) => { @@ -209,6 +213,8 @@ 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"); @@ -565,7 +571,7 @@ impl<'a> CanvasPaintThread<'a> { }) } - fn send_data(&mut self, chan: IpcSender<CanvasImageData>) { + fn send_data(&mut self, chan: IpcSender<CanvasData>) { self.drawtarget.snapshot().get_data_surface().with_data(|element| { let size = self.drawtarget.get_size(); @@ -608,7 +614,7 @@ impl<'a> CanvasPaintThread<'a> { let data = CanvasImageData { image_key: self.image_key.unwrap(), }; - chan.send(data).unwrap(); + chan.send(CanvasData::Image(data)).unwrap(); }) } diff --git a/components/canvas/gl_context.rs b/components/canvas/gl_context.rs deleted file mode 100644 index 69a26c0e03c..00000000000 --- a/components/canvas/gl_context.rs +++ /dev/null @@ -1,203 +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::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 b9f0823a07d..a3d90909637 100644 --- a/components/canvas/lib.rs +++ b/components/canvas/lib.rs @@ -6,18 +6,16 @@ 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 webrender; +extern crate servo_config; extern crate webrender_api; pub mod canvas_paint_thread; -pub mod gl_context; -mod webgl_mode; -pub mod webgl_thread; +pub mod webgl_paint_thread; diff --git a/components/canvas/webgl_mode/inprocess.rs b/components/canvas/webgl_mode/inprocess.rs deleted file mode 100644 index 257f1395545..00000000000 --- a/components/canvas/webgl_mode/inprocess.rs +++ /dev/null @@ -1,95 +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 ::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 deleted file mode 100644 index 660818fb096..00000000000 --- a/components/canvas/webgl_mode/mod.rs +++ /dev/null @@ -1,6 +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/. */ - -mod inprocess; -pub use self::inprocess::WebGLThreads; diff --git a/components/canvas/webgl_paint_thread.rs b/components/canvas/webgl_paint_thread.rs new file mode 100644 index 00000000000..2b6819effba --- /dev/null +++ b/components/canvas/webgl_paint_thread.rs @@ -0,0 +1,379 @@ +/* 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 deleted file mode 100644 index 907233a3b89..00000000000 --- a/components/canvas/webgl_thread.rs +++ /dev/null @@ -1,1204 +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::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()); - } -} |