/* 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 https://mozilla.org/MPL/2.0/. */ use super::webgl_thread::{GLState, WebGLImpl}; use canvas_traits::webgl::{ GLContextAttributes, GLLimits, WebGLCommand, WebGLCommandBacktrace, WebGLVersion, }; use compositing::compositor_thread::{self, CompositorProxy}; use euclid::Size2D; use gleam::gl; use offscreen_gl_context::{ ColorAttachmentType, DrawBuffer, GLContext, GLContextAttributes as RawGLContextAttributes, GLContextDispatcher, }; use offscreen_gl_context::{GLLimits as RawGLLimits, GLVersion}; use offscreen_gl_context::{NativeGLContext, NativeGLContextHandle, NativeGLContextMethods}; use offscreen_gl_context::{OSMesaContext, OSMesaContextHandle}; use std::sync::{Arc, Mutex}; /// 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), 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 { // FIXME(emilio): This assumes a single GL backend per platform which is // not true on Linux, we probably need a third `Egl` variant or abstract // it a bit more... 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()))) } 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 { OSMesaContext::current_handle().map(GLContextFactory::OSMesa) } /// Creates a new shared GLContext with the main GLContext pub fn new_shared_context( &self, webgl_version: WebGLVersion, size: Size2D, attributes: GLContextAttributes, ) -> Result { let attributes = map_attrs(attributes); Ok(match *self { GLContextFactory::Native(ref handle, ref dispatcher) => { let dispatcher = dispatcher.as_ref().map(|d| Box::new(d.clone()) as Box<_>); GLContextWrapper::Native(GLContext::new_shared_with_dispatcher( // FIXME(nox): Why are those i32 values? size.to_i32(), attributes, ColorAttachmentType::Texture, gl::GlType::default(), Self::gl_version(webgl_version), Some(handle), dispatcher, )?) }, GLContextFactory::OSMesa(ref handle) => { GLContextWrapper::OSMesa(GLContext::new_shared_with_dispatcher( // FIXME(nox): Why are those i32 values? size.to_i32(), attributes, ColorAttachmentType::Texture, gl::GlType::default(), Self::gl_version(webgl_version), Some(handle), None, )?) }, }) } /// Creates a new non-shared GLContext pub fn new_context( &self, webgl_version: WebGLVersion, size: Size2D, attributes: GLContextAttributes, ) -> Result { let attributes = map_attrs(attributes); Ok(match *self { GLContextFactory::Native(..) => { GLContextWrapper::Native(GLContext::new_shared_with_dispatcher( // FIXME(nox): Why are those i32 values? size.to_i32(), attributes, ColorAttachmentType::Texture, gl::GlType::default(), Self::gl_version(webgl_version), None, None, )?) }, GLContextFactory::OSMesa(_) => { GLContextWrapper::OSMesa(GLContext::new_shared_with_dispatcher( // FIXME(nox): Why are those i32 values? size.to_i32(), attributes, ColorAttachmentType::Texture, gl::GlType::default(), Self::gl_version(webgl_version), None, None, )?) }, }) } fn gl_version(webgl_version: WebGLVersion) -> GLVersion { match webgl_version { WebGLVersion::WebGL1 => GLVersion::Major(2), WebGLVersion::WebGL2 => GLVersion::Major(3), } } } /// GLContextWrapper used to abstract NativeGLContext and OSMesaContext types pub enum GLContextWrapper { Native(GLContext), OSMesa(GLContext), } 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, backtrace: WebGLCommandBacktrace, state: &mut GLState, ) { match *self { GLContextWrapper::Native(ref ctx) => { WebGLImpl::apply(ctx, state, cmd, backtrace); }, GLContextWrapper::OSMesa(ref ctx) => { WebGLImpl::apply(ctx, state, cmd, backtrace); }, } } pub fn gl(&self) -> &dyn gl::Gl { match *self { GLContextWrapper::Native(ref ctx) => ctx.gl(), GLContextWrapper::OSMesa(ref ctx) => ctx.gl(), } } pub fn get_info(&self) -> (Size2D, 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, map_limits(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, map_limits(limits)) }, } } pub fn resize(&mut self, size: Size2D) -> Result { match *self { GLContextWrapper::Native(ref mut ctx) => { // FIXME(nox): Why are those i32 values? ctx.resize(size.to_i32()) }, GLContextWrapper::OSMesa(ref mut ctx) => { // FIXME(nox): Why are those i32 values? ctx.resize(size.to_i32()) }, } } } /// 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>, } impl MainThreadDispatcher { fn new(proxy: CompositorProxy) -> Self { Self { compositor_proxy: Arc::new(Mutex::new(proxy)), } } } impl GLContextDispatcher for MainThreadDispatcher { fn dispatch(&self, f: Box) { self.compositor_proxy .lock() .unwrap() .send(compositor_thread::Msg::Dispatch(f)); } } fn map_limits(limits: RawGLLimits) -> GLLimits { GLLimits { max_vertex_attribs: limits.max_vertex_attribs, max_tex_size: limits.max_tex_size, max_cube_map_tex_size: limits.max_cube_map_tex_size, max_combined_texture_image_units: limits.max_combined_texture_image_units, max_fragment_uniform_vectors: limits.max_fragment_uniform_vectors, max_renderbuffer_size: limits.max_renderbuffer_size, max_texture_image_units: limits.max_texture_image_units, max_varying_vectors: limits.max_varying_vectors, max_vertex_texture_image_units: limits.max_vertex_texture_image_units, max_vertex_uniform_vectors: limits.max_vertex_uniform_vectors, } } pub fn map_attrs(attrs: GLContextAttributes) -> RawGLContextAttributes { RawGLContextAttributes { alpha: attrs.alpha, depth: attrs.depth, stencil: attrs.stencil, antialias: attrs.antialias, premultiplied_alpha: attrs.premultiplied_alpha, preserve_drawing_buffer: attrs.preserve_drawing_buffer, } } pub fn map_attrs_to_script_attrs(attrs: RawGLContextAttributes) -> GLContextAttributes { GLContextAttributes { alpha: attrs.alpha, depth: attrs.depth, stencil: attrs.stencil, antialias: attrs.antialias, premultiplied_alpha: attrs.premultiplied_alpha, preserve_drawing_buffer: attrs.preserve_drawing_buffer, } }