diff --git a/src/gl_context.rs b/src/gl_context.rs index a4d81b3..0bce1eb 100644 --- a/src/gl_context.rs +++ b/src/gl_context.rs @@ -29,7 +29,13 @@ impl GLContext where Native: NativeGLContextMethods { pub fn create(shared_with: Option<&Native::Handle>) -> Result, &'static str> { - let native_context = try!(Native::create_shared(shared_with)); + Self::create_shared_with_dispatcher(shared_with, None) + } + + pub fn create_shared_with_dispatcher(shared_with: Option<&Native::Handle>, + dispatcher: Option>) + -> Result, &'static str> { + let native_context = try!(Native::create_shared_with_dispatcher(shared_with, dispatcher)); try!(native_context.make_current()); let attributes = GLContextAttributes::any(); let formats = GLFormats::detect(&attributes); @@ -45,7 +51,6 @@ impl GLContext }) } - #[inline(always)] pub fn get_proc_address(addr: &str) -> *const () { Native::get_proc_address(addr) @@ -61,9 +66,18 @@ impl GLContext color_attachment_type: ColorAttachmentType, shared_with: Option<&Native::Handle>) -> Result, &'static str> { + Self::new_shared_with_dispatcher(size, attributes, color_attachment_type, shared_with, None) + } + + pub fn new_shared_with_dispatcher(size: Size2D, + attributes: GLContextAttributes, + color_attachment_type: ColorAttachmentType, + shared_with: Option<&Native::Handle>, + dispatcher: Option>) + -> Result, &'static str> { // We create a headless context with a dummy size, we're painting to the // draw_buffer's framebuffer anyways. - let mut context = try!(Self::create(shared_with)); + let mut context = try!(Self::create_shared_with_dispatcher(shared_with, dispatcher)); context.formats = GLFormats::detect(&attributes); context.attributes = attributes; @@ -151,6 +165,12 @@ impl GLContext } } +// Dispatches functions to the thread where a NativeGLContext is bound. +// Right now it's used in the WGL implementation to dispatch functions to the thread +// where the context we share from is bound. See the WGL implementation for more details. +pub trait GLContextDispatcher { + fn dispatch(&self, Box); +} trait GLContextPrivateMethods { fn init_offscreen(&mut self, Size2D, ColorAttachmentType) -> Result<(), &'static str>; diff --git a/src/lib.rs b/src/lib.rs index 6d99464..67de20e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,7 +33,7 @@ pub use platform::{NativeGLContext, NativeGLContextMethods, NativeGLContextHandl pub use platform::{OSMesaContext, OSMesaContextHandle}; mod gl_context; -pub use gl_context::GLContext; +pub use gl_context::{GLContext, GLContextDispatcher}; mod draw_buffer; pub use draw_buffer::{DrawBuffer, ColorAttachmentType}; diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 46ca6e0..8fa6a90 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -1,3 +1,5 @@ +use gl_context::GLContextDispatcher; + pub trait NativeGLContextMethods: Sized { type Handle; @@ -9,6 +11,11 @@ pub trait NativeGLContextMethods: Sized { fn create_shared(with: Option<&Self::Handle>) -> Result; + fn create_shared_with_dispatcher(with: Option<&Self::Handle>, _dispatcher: Option>) + -> Result { + Self::create_shared(with) + } + fn create_headless() -> Result { Self::create_shared(None) } diff --git a/src/platform/with_wgl/native_gl_context.rs b/src/platform/with_wgl/native_gl_context.rs index 399b269..b8d109d 100644 --- a/src/platform/with_wgl/native_gl_context.rs +++ b/src/platform/with_wgl/native_gl_context.rs @@ -1,7 +1,9 @@ use platform::NativeGLContextMethods; +use gl_context::GLContextDispatcher; use std::ffi::CString; use std::os::raw::c_void; use std::ptr; +use std::sync::mpsc; use winapi; use user32; @@ -108,10 +110,34 @@ impl NativeGLContextMethods for NativeGLContext { } fn create_shared(with: Option<&Self::Handle>) -> Result { - let render_ctx = match with { - Some(ref handle) => handle.0, - None => ptr::null_mut(), + Self::create_shared_with_dispatcher(with, None) + } + + fn create_shared_with_dispatcher(with: Option<&Self::Handle>, dispatcher: Option>) + -> Result { + let (render_ctx, device_ctx) = match with { + Some(ref handle) => (handle.0, handle.1), + None => (ptr::null_mut(), ptr::null_mut()) }; + + if let Some(ref dispatcher) = dispatcher { + // wglShareLists fails if the context to share is current in a different thread. + // Additionally wglMakeCurrent cannot 'steal' a context that is current in other thread, so + // we have to unbind the shared context in its own thread, call wglShareLists in this thread + // and bind the original share context again when the wglShareList is completed. + let (tx, rx) = mpsc::channel(); + dispatcher.dispatch(Box::new(move || { + unsafe { + if wgl::MakeCurrent(ptr::null_mut(), ptr::null_mut()) == 0 { + error!("WGL MakeCurrent failed"); + } + } + tx.send(()).unwrap(); + })); + // Wait until wglMakeCurrent operation is completed in the thread of the shared context + rx.recv().unwrap(); + } + match unsafe { utils::create_offscreen(render_ctx, &WGLAttributes::default()) } { Ok(ref res) => { let ctx = NativeGLContext { @@ -119,6 +145,23 @@ impl NativeGLContextMethods for NativeGLContext { device_ctx: res.1, weak: false, }; + + // Restore shared context + if let Some(ref dispatcher) = dispatcher { + let (tx, rx) = mpsc::channel(); + let handle = NativeGLContextHandle(render_ctx, device_ctx); + dispatcher.dispatch(Box::new(move || { + unsafe { + if wgl::MakeCurrent(handle.1 as *const _, handle.0 as *const _) == 0 { + error!("WGL MakeCurrent failed!"); + } + }; + tx.send(()).unwrap(); + })); + // Wait until wglMakeCurrent operation is completed in the thread of the shared context + rx.recv().unwrap(); + } + Ok(ctx) } Err(s) => {