diff --git a/build.rs b/build.rs index c21f4f6a..f62dc931 100644 --- a/build.rs +++ b/build.rs @@ -25,6 +25,7 @@ fn main() { vec![ "WGL_ARB_create_context".to_string(), "WGL_ARB_create_context_profile".to_string(), + "WGL_ARB_create_context_robustness".to_string(), "WGL_ARB_extensions_string".to_string(), "WGL_ARB_framebuffer_sRGB".to_string(), "WGL_ARB_multisample".to_string(), @@ -42,7 +43,8 @@ fn main() { gl_generator::Fallbacks::All, khronos_api::EGL_XML, vec![ - "EGL_KHR_create_context".to_string() + "EGL_KHR_create_context".to_string(), + "EGL_EXT_create_context_robustness".to_string(), ], "1.5", "core", &mut file).unwrap(); } @@ -63,6 +65,7 @@ fn main() { vec![ "GLX_ARB_create_context".to_string(), "GLX_ARB_create_context_profile".to_string(), + "GLX_ARB_create_context_robustness".to_string(), "GLX_ARB_framebuffer_sRGB".to_string(), "GLX_EXT_framebuffer_sRGB".to_string(), "GLX_EXT_swap_control".to_string(), @@ -76,7 +79,8 @@ fn main() { gl_generator::Fallbacks::All, khronos_api::EGL_XML, vec![ - "EGL_KHR_create_context".to_string() + "EGL_KHR_create_context".to_string(), + "EGL_EXT_create_context_robustness".to_string(), ], "1.5", "core", &mut file).unwrap(); } diff --git a/src/api/cocoa/mod.rs b/src/api/cocoa/mod.rs index 772b9065..332244ea 100644 --- a/src/api/cocoa/mod.rs +++ b/src/api/cocoa/mod.rs @@ -13,6 +13,7 @@ use GlContext; use GlProfile; use GlRequest; use PixelFormat; +use Robustness; use native_monitor::NativeMonitorId; use objc::runtime::{Class, Object, Sel, BOOL, YES, NO}; @@ -332,6 +333,13 @@ impl Window { unimplemented!() } + match builder.gl_robustness { + Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { + return Err(CreationError::NotSupported); + }, + _ => () + } + let app = match Window::create_app() { Some(app) => app, None => { return Err(OsError(format!("Couldn't create NSApplication"))); }, diff --git a/src/api/egl/mod.rs b/src/api/egl/mod.rs index 7b31017b..07c70b0d 100644 --- a/src/api/egl/mod.rs +++ b/src/api/egl/mod.rs @@ -7,6 +7,7 @@ use CreationError; use GlContext; use GlRequest; use PixelFormat; +use Robustness; use Api; use libc; @@ -116,15 +117,16 @@ impl Context { let context = unsafe { if let Some(version) = version { try!(create_context(&egl, display, &egl_version, api, version, config_id, - builder.gl_debug).map_err(|_| CreationError::NotSupported)) + builder.gl_debug, builder.gl_robustness)) } else if api == Api::OpenGlEs { if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (2, 0), - config_id, builder.gl_debug) + config_id, builder.gl_debug, builder.gl_robustness) { ctxt } else if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (1, 0), - config_id, builder.gl_debug) + config_id, builder.gl_debug, + builder.gl_robustness) { ctxt } else { @@ -133,15 +135,17 @@ impl Context { } else { if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (3, 2), - config_id, builder.gl_debug) + config_id, builder.gl_debug, builder.gl_robustness) { ctxt } else if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (3, 1), - config_id, builder.gl_debug) + config_id, builder.gl_debug, + builder.gl_robustness) { ctxt } else if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (1, 0), - config_id, builder.gl_debug) + config_id, builder.gl_debug, + builder.gl_robustness) { ctxt } else { @@ -335,8 +339,8 @@ unsafe fn enumerate_configs(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDi unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDisplay, egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint), api: Api, version: (u8, u8), config_id: ffi::egl::types::EGLConfig, - gl_debug: bool) - -> Result + gl_debug: bool, gl_robustness: Robustness) + -> Result { let extensions = if egl_version >= &(1, 2) { let p = CStr::from_ptr(egl.QueryString(display, ffi::egl::EXTENSIONS as i32)); @@ -346,6 +350,7 @@ unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDispl }; let mut context_attributes = vec![]; + let mut flags = 0; if egl_version >= &(1, 5) || extensions.contains("EGL_KHR_create_context ") || @@ -356,17 +361,52 @@ unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDispl context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32); context_attributes.push(version.1 as i32); + if egl_version >= &(1, 5) || + extensions.contains("EGL_EXT_create_context_robustness ") || + extensions.ends_with("EGL_EXT_create_context_robustness") + { + match gl_robustness { + Robustness::RobustNoResetNotification | Robustness::TryRobustNoResetNotification => { + context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as libc::c_int); + context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as libc::c_int); + flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as libc::c_int; + }, + Robustness::RobustLoseContextOnReset | Robustness::TryRobustLoseContextOnReset => { + context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY as libc::c_int); + context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as libc::c_int); + flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as libc::c_int; + }, + Robustness::NotRobust => () + } + } else { + match gl_robustness { + Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { + return Err(CreationError::NotSupported); + }, + _ => () + } + } + if gl_debug { if egl_version >= &(1, 5) { context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32); context_attributes.push(ffi::egl::TRUE as i32); } else { - context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32); - context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG_BIT_KHR as i32); + flags = flags | ffi::egl::CONTEXT_OPENGL_DEBUG_BIT_KHR as i32; } } + context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32); + context_attributes.push(flags); + } else if egl_version >= &(1, 3) && api == Api::OpenGlEs { + match gl_robustness { + Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { + return Err(CreationError::NotSupported); + }, + _ => () + } + context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32); context_attributes.push(version.0 as i32); } @@ -377,7 +417,7 @@ unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDispl context_attributes.as_ptr()); if context.is_null() { - return Err(()); + return Err(CreationError::NotSupported); } Ok(context) diff --git a/src/api/glx/mod.rs b/src/api/glx/mod.rs index 455f6a53..2c15e181 100644 --- a/src/api/glx/mod.rs +++ b/src/api/glx/mod.rs @@ -8,9 +8,10 @@ use GlProfile; use GlRequest; use Api; use PixelFormat; +use Robustness; use libc; -use std::ffi::CString; +use std::ffi::{CStr, CString}; use std::{mem, ptr}; use api::x11::ffi; @@ -48,6 +49,13 @@ impl Context { ptr::null() }; + // loading the list of extensions + let extensions = unsafe { + let extensions = glx.QueryExtensionsString(display as *mut _, 0); // FIXME: screen number + let extensions = CStr::from_ptr(extensions).to_bytes().to_vec(); + String::from_utf8(extensions).unwrap() + }; + // loading the extra GLX functions let extra_functions = ffi::glx_extra::Glx::load_with(|addr| { with_c_str(addr, |s| { @@ -58,32 +66,35 @@ impl Context { // creating GL context let context = match builder.gl_version { GlRequest::Latest => { - if let Ok(ctxt) = create_context(&glx, &extra_functions, (3, 2), - builder.gl_profile, builder.gl_debug, share, + if let Ok(ctxt) = create_context(&glx, &extra_functions, &extensions, (3, 2), + builder.gl_profile, builder.gl_debug, + builder.gl_robustness, share, display, fb_config, &mut visual_infos) { ctxt - } else if let Ok(ctxt) = create_context(&glx, &extra_functions, (3, 1), + } else if let Ok(ctxt) = create_context(&glx, &extra_functions, &extensions, (3, 1), builder.gl_profile, builder.gl_debug, - share, display, fb_config, - &mut visual_infos) + builder.gl_robustness, share, display, + fb_config, &mut visual_infos) { ctxt } else { - try!(create_context(&glx, &extra_functions, (1, 0), builder.gl_profile, - builder.gl_debug, share, display, fb_config, - &mut visual_infos)) + try!(create_context(&glx, &extra_functions, &extensions, (1, 0), + builder.gl_profile, builder.gl_debug, builder.gl_robustness, + share, display, fb_config, &mut visual_infos)) } }, GlRequest::Specific(Api::OpenGl, (major, minor)) => { - try!(create_context(&glx, &extra_functions, (major, minor), builder.gl_profile, - builder.gl_debug, share, display, fb_config, &mut visual_infos)) + try!(create_context(&glx, &extra_functions, &extensions, (major, minor), + builder.gl_profile, builder.gl_debug, builder.gl_robustness, + share, display, fb_config, &mut visual_infos)) }, GlRequest::Specific(_, _) => panic!("Only OpenGL is supported"), GlRequest::GlThenGles { opengl_version: (major, minor), .. } => { - try!(create_context(&glx, &extra_functions, (major, minor), builder.gl_profile, - builder.gl_debug, share, display, fb_config, &mut visual_infos)) + try!(create_context(&glx, &extra_functions, &extensions, (major, minor), + builder.gl_profile, builder.gl_debug, builder.gl_robustness, + share, display, fb_config, &mut visual_infos)) }, }; @@ -191,9 +202,9 @@ impl Drop for Context { } } -fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, +fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, extensions: &str, version: (u8, u8), profile: Option, debug: bool, - share: ffi::GLXContext, display: *mut ffi::Display, + robustness: Robustness, share: ffi::GLXContext, display: *mut ffi::Display, fb_config: ffi::glx::types::GLXFBConfig, visual_infos: &mut ffi::glx::types::XVisualInfo) -> Result @@ -219,10 +230,42 @@ fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, attributes.push(flag as libc::c_int); } - if debug { - attributes.push(ffi::glx_extra::CONTEXT_FLAGS_ARB as libc::c_int); - attributes.push(ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int); - } + let flags = { + let mut flags = 0; + + // robustness + if extensions.split(' ').find(|&i| i == "GLX_ARB_create_context_robustness").is_some() { + match robustness { + Robustness::RobustNoResetNotification | Robustness::TryRobustNoResetNotification => { + attributes.push(ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as libc::c_int); + attributes.push(ffi::glx_extra::NO_RESET_NOTIFICATION_ARB as libc::c_int); + flags = flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as libc::c_int; + }, + Robustness::RobustLoseContextOnReset | Robustness::TryRobustLoseContextOnReset => { + attributes.push(ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as libc::c_int); + attributes.push(ffi::glx_extra::LOSE_CONTEXT_ON_RESET_ARB as libc::c_int); + flags = flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as libc::c_int; + }, + Robustness::NotRobust => () + } + } else { + match robustness { + Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { + return Err(CreationError::NotSupported); + }, + _ => () + } + } + + if debug { + flags = flags | ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int; + } + + flags + }; + + attributes.push(ffi::glx_extra::CONTEXT_FLAGS_ARB as libc::c_int); + attributes.push(flags); attributes.push(0); diff --git a/src/api/osmesa/mod.rs b/src/api/osmesa/mod.rs index daaba52e..db0a1e7d 100644 --- a/src/api/osmesa/mod.rs +++ b/src/api/osmesa/mod.rs @@ -8,6 +8,7 @@ use ContextError; use CreationError; use GlContext; use PixelFormat; +use Robustness; use libc; use std::{mem, ptr}; use std::ffi::CString; @@ -38,6 +39,13 @@ impl OsMesaContext { let dimensions = builder.dimensions.unwrap(); + match builder.gl_robustness { + Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { + return Err(CreationError::NotSupported.into()); + }, + _ => () + } + Ok(OsMesaContext { width: dimensions.0, height: dimensions.1, diff --git a/src/api/wgl/mod.rs b/src/api/wgl/mod.rs index c33054e3..ee8aa3b2 100644 --- a/src/api/wgl/mod.rs +++ b/src/api/wgl/mod.rs @@ -7,6 +7,7 @@ use GlContext; use GlRequest; use GlProfile; use PixelFormat; +use Robustness; use Api; use self::make_current_guard::CurrentContextGuard; @@ -266,10 +267,42 @@ unsafe fn create_context(extra: Option<(&gl::wgl_extra::Wgl, &BuilderAttribs<'st } } - if builder.gl_debug { - attributes.push(gl::wgl_extra::CONTEXT_FLAGS_ARB as libc::c_int); - attributes.push(gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int); - } + let flags = { + let mut flags = 0; + + // robustness + if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context_robustness").is_some() { + match builder.gl_robustness { + Robustness::RobustNoResetNotification | Robustness::TryRobustNoResetNotification => { + attributes.push(gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as libc::c_int); + attributes.push(gl::wgl_extra::NO_RESET_NOTIFICATION_ARB as libc::c_int); + flags = flags | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as libc::c_int; + }, + Robustness::RobustLoseContextOnReset | Robustness::TryRobustLoseContextOnReset => { + attributes.push(gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as libc::c_int); + attributes.push(gl::wgl_extra::LOSE_CONTEXT_ON_RESET_ARB as libc::c_int); + flags = flags | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as libc::c_int; + }, + Robustness::NotRobust => () + } + } else { + match builder.gl_robustness { + Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { + return Err(CreationError::NotSupported); + }, + _ => () + } + } + + if builder.gl_debug { + flags = flags | gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int; + } + + flags + }; + + attributes.push(gl::wgl_extra::CONTEXT_FLAGS_ARB as libc::c_int); + attributes.push(flags); attributes.push(0); diff --git a/src/headless.rs b/src/headless.rs index 8ee81d88..69f9aab2 100644 --- a/src/headless.rs +++ b/src/headless.rs @@ -5,6 +5,7 @@ use CreationError; use GlRequest; use GlContext; use PixelFormat; +use Robustness; use gl_common; use libc; @@ -43,6 +44,12 @@ impl HeadlessRendererBuilder { self } + /// Sets the robustness of the OpenGL context. See the docs of `Robustness`. + pub fn with_gl_robustness(mut self, robustness: Robustness) -> HeadlessRendererBuilder { + self.attribs.gl_robustness = robustness; + self + } + /// Builds the headless context. /// /// Error should be very rare and only occur in case of permission denied, incompatible system, diff --git a/src/lib.rs b/src/lib.rs index a6464360..64b0acef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -197,6 +197,33 @@ impl GlRequest { /// the compatibility profile features. pub static GL_CORE: GlRequest = GlRequest::Specific(Api::OpenGl, (3, 2)); +/// Specifies the tolerance of the OpenGL context to faults. If you accept raw OpenGL commands +/// and/or raw shader code from an untrusted source, you should definitely care about this. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Robustness { + /// Not everything is checked. Your application can crash if you do something wrong with your + /// shaders. + NotRobust, + + /// Everything is checked to avoid any crash. The driver will attempt to avoid any problem, + /// but if a problem occurs the behavior is implementation-defined. You are just guaranteed not + /// to get a crash. + RobustNoResetNotification, + + /// Same as `RobustNoResetNotification` but the context creation doesn't fail if it's not + /// supported. + TryRobustNoResetNotification, + + /// Everything is checked to avoid any crash. If a problem occurs, the context will enter a + /// "context lost" state. It must then be recreated. For the moment, glutin doesn't provide a + /// way to recreate a context with the same window :-/ + RobustLoseContextOnReset, + + /// Same as `RobustLoseContextOnReset` but the context creation doesn't fail if it's not + /// supported. + TryRobustLoseContextOnReset, +} + #[derive(Debug, Copy, Clone)] pub enum MouseCursor { /// The platform-dependent default cursor. @@ -299,6 +326,7 @@ pub struct BuilderAttribs<'a> { gl_version: GlRequest, gl_profile: Option, gl_debug: bool, + gl_robustness: Robustness, vsync: bool, visible: bool, multisampling: Option, @@ -324,6 +352,7 @@ impl BuilderAttribs<'static> { gl_version: GlRequest::Latest, gl_profile: None, gl_debug: cfg!(debug_assertions), + gl_robustness: Robustness::NotRobust, vsync: false, visible: true, multisampling: None, @@ -354,6 +383,7 @@ impl<'a> BuilderAttribs<'a> { gl_version: self.gl_version, gl_profile: self.gl_profile, gl_debug: self.gl_debug, + gl_robustness: self.gl_robustness, vsync: self.vsync, visible: self.visible, multisampling: self.multisampling, diff --git a/src/window.rs b/src/window.rs index 485ba58c..96852871 100644 --- a/src/window.rs +++ b/src/window.rs @@ -12,6 +12,7 @@ use GlProfile; use GlRequest; use MouseCursor; use PixelFormat; +use Robustness; use native_monitor::NativeMonitorId; use gl_common; @@ -84,6 +85,12 @@ impl<'a> WindowBuilder<'a> { self } + /// Sets the robustness of the OpenGL context. See the docs of `Robustness`. + pub fn with_gl_robustness(mut self, robustness: Robustness) -> WindowBuilder<'a> { + self.attribs.gl_robustness = robustness; + self + } + /// Requests that the window has vsync enabled. pub fn with_vsync(mut self) -> WindowBuilder<'a> { self.attribs.vsync = true;