From 29c72814f9e794c42236160d88c715bec4ee6a26 Mon Sep 17 00:00:00 2001 From: Imanol Fernandez Date: Mon, 19 Sep 2016 14:11:46 +0200 Subject: [PATCH 1/7] Implement WGL --- Cargo.toml | 7 + build.rs | 27 + src/lib.rs | 11 + src/platform/mod.rs | 9 +- src/platform/with_wgl/mod.rs | 16 + src/platform/with_wgl/native_gl_context.rs | 144 +++++ src/platform/with_wgl/utils.rs | 692 +++++++++++++++++++++ src/platform/with_wgl/wgl_attributes.rs | 83 +++ 8 files changed, 987 insertions(+), 2 deletions(-) create mode 100644 src/platform/with_wgl/mod.rs create mode 100644 src/platform/with_wgl/native_gl_context.rs create mode 100644 src/platform/with_wgl/utils.rs create mode 100644 src/platform/with_wgl/wgl_attributes.rs diff --git a/Cargo.toml b/Cargo.toml index 25700a7..e064239 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,3 +32,10 @@ cgl = "0.1" [target.'cfg(target_os = "linux")'.dependencies.x11] version = "2.3.0" features = ["xlib"] + +[target.'cfg(target_os = "windows")'.dependencies] +winapi = "0.2" +gdi32-sys = "0.2" +user32-sys = "0.2" +kernel32-sys = "0.2" +lazy_static = "0.2" diff --git a/build.rs b/build.rs index 4a7782d..5f14f4b 100644 --- a/build.rs +++ b/build.rs @@ -21,4 +21,31 @@ fn main() { .write_bindings(gl_generator::StaticGenerator, &mut file).unwrap(); println!("cargo:rustc-link-lib=EGL"); } + + if target.contains("windows") { + let mut file = File::create(&dest.join("wgl_bindings.rs")).unwrap(); + Registry::new(Api::Wgl, (1, 0), Profile::Core, Fallbacks::All, []) + .write_bindings(gl_generator::StaticGenerator, &mut file) + .unwrap(); + + let mut file = File::create(&dest.join("wgl_extra_bindings.rs")).unwrap(); + Registry::new(Api::Wgl, (1, 0), Profile::Core, Fallbacks::All, [ + "WGL_ARB_create_context", + "WGL_ARB_create_context_profile", + "WGL_ARB_create_context_robustness", + "WGL_ARB_context_flush_control", + "WGL_ARB_extensions_string", + "WGL_ARB_framebuffer_sRGB", + "WGL_ARB_multisample", + "WGL_ARB_pixel_format", + "WGL_ARB_pixel_format_float", + "WGL_EXT_create_context_es2_profile", + "WGL_EXT_extensions_string", + "WGL_EXT_framebuffer_sRGB", + "WGL_EXT_swap_control", + ]) + .write_bindings(gl_generator::StructGenerator, &mut file).unwrap(); + + + } } diff --git a/src/lib.rs b/src/lib.rs index 49af5dc..6d99464 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,17 @@ extern crate cgl; extern crate core_foundation; #[cfg(feature="osmesa")] extern crate osmesa_sys; +#[cfg(target_os = "windows")] +extern crate winapi; +#[cfg(target_os = "windows")] +extern crate kernel32; +#[cfg(target_os = "windows")] +extern crate gdi32; +#[cfg(target_os = "windows")] +extern crate user32; +#[cfg(target_os = "windows")] +#[macro_use] +extern crate lazy_static; mod platform; pub use platform::{NativeGLContext, NativeGLContextMethods, NativeGLContextHandle}; diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 10d35d5..46ca6e0 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -39,7 +39,12 @@ pub mod with_cgl; #[cfg(target_os="macos")] pub use self::with_cgl::{NativeGLContext, NativeGLContextHandle}; -#[cfg(not(any(target_os="linux", target_os="android", target_os="macos")))] +#[cfg(target_os="windows")] +pub mod with_wgl; +#[cfg(target_os="windows")] +pub use self::with_wgl::{NativeGLContext, NativeGLContextHandle}; + +#[cfg(not(any(target_os="linux", target_os="android", target_os="macos", target_os="windows")))] pub mod not_implemented; -#[cfg(not(any(target_os="linux", target_os="android", target_os="macos")))] +#[cfg(not(any(target_os="linux", target_os="android", target_os="macos", target_os="windows")))] pub use self::not_implemented::{NativeGLContext, NativeGLContextHandle}; diff --git a/src/platform/with_wgl/mod.rs b/src/platform/with_wgl/mod.rs new file mode 100644 index 0000000..2e8cf8b --- /dev/null +++ b/src/platform/with_wgl/mod.rs @@ -0,0 +1,16 @@ +/// WGL bindings +pub mod wgl { + include!(concat!(env!("OUT_DIR"), "/wgl_bindings.rs")); +} + +/// Functions that are not necessarly always available +pub mod wgl_ext { + include!(concat!(env!("OUT_DIR"), "/wgl_extra_bindings.rs")); +} + +mod wgl_attributes; +mod native_gl_context; +mod utils; +pub use self::native_gl_context::NativeGLContext; +pub use self::native_gl_context::NativeGLContextHandle; + diff --git a/src/platform/with_wgl/native_gl_context.rs b/src/platform/with_wgl/native_gl_context.rs new file mode 100644 index 0000000..1d8d42d --- /dev/null +++ b/src/platform/with_wgl/native_gl_context.rs @@ -0,0 +1,144 @@ +use platform::NativeGLContextMethods; +use std::ffi::CString; +use std::os::raw::{c_void}; +use std::ptr; +use std::sync::{Once, ONCE_INIT}; + +use winapi; +use user32; +use kernel32; +use super::wgl; +use super::wgl_attributes::*; +use gleam::gl; + +/// Wrapper to satisfy `Sync`. +struct HMODULEWrapper(winapi::HMODULE); +unsafe impl Sync for HMODULEWrapper {} + +lazy_static! { + static ref GL_LIB: Option = { + let p = unsafe{kernel32::LoadLibraryA(b"opengl32.dll\0".as_ptr() as *const _)}; + if p.is_null() { + debug!("opengl32.dll not found!"); + None + } + else { + debug!("opengl32.dll LOADED!"); + Some(HMODULEWrapper(p)) + } + }; +} + +static LOAD_GL: Once = ONCE_INIT; +pub fn load_gl() { + LOAD_GL.call_once(|| { + gl::load_with(|s| { + NativeGLContext::get_proc_address(s) as *const _ + }); + }); +} + +pub struct NativeGLContext { + pub render_ctx: winapi::HGLRC, + pub device_ctx:winapi::HDC, // + pub weak: bool, +} + +impl Drop for NativeGLContext { + fn drop(&mut self) { + unsafe { + if !self.weak { + wgl::DeleteContext(self.render_ctx as *const _); + let window = user32::WindowFromDC(self.device_ctx); + user32::ReleaseDC(window, self.device_ctx); + user32::DestroyWindow(window); + } + } + } +} + +pub struct NativeGLContextHandle(pub winapi::HGLRC, pub winapi::HDC); +unsafe impl Send for NativeGLContextHandle {} + +impl NativeGLContextMethods for NativeGLContext { + type Handle = NativeGLContextHandle; + + fn get_proc_address(addr: &str) -> *const () { + let addr = CString::new(addr.as_bytes()).unwrap().as_ptr(); + unsafe { + let p = wgl::GetProcAddress(addr) as *const _; + if !p.is_null() { return p; } + match *GL_LIB { + Some(ref lib) => kernel32::GetProcAddress(lib.0, addr) as *const _, + None => ptr::null_mut() + } + } + + } + + fn create_shared(with: Option<&Self::Handle>) -> Result { + match unsafe{super::utils::create_offscreen(with, &WGLAttributes::default())}{ + Ok(ctx) => { + //wglGetProcAddress only works in the presence of a valid GL context + //OpenGL functions must be loaded after the first context is created + ctx.make_current().unwrap(); + load_gl(); + Ok(ctx) + } + Err(s) => { + error!("WGL: {}", s); + Err("Error creating WGL context") + } + } + } + + fn is_current(&self) -> bool { + unsafe { wgl::GetCurrentContext() == self.render_ctx as *const c_void } + } + + fn current() -> Option { + if let Some(handle) = Self::current_handle() { + Some(NativeGLContext { + render_ctx: handle.0, + device_ctx: handle.1, + weak: true + }) + } + else { + None + } + } + + fn current_handle() -> Option { + let handle = unsafe{ wgl::GetCurrentContext()}; + if !handle.is_null() { + let hdc = unsafe { wgl::GetCurrentDC()}; + Some(NativeGLContextHandle(handle as winapi::HGLRC, hdc as winapi::HDC)) + } + else { + None + } + } + + fn make_current(&self) -> Result<(), &'static str> { + if unsafe {wgl::MakeCurrent(self.device_ctx as * const _, self.render_ctx as *const _) != 0 } { + Ok(()) + } else { + Err("wgl::makeCurrent failed") + } + } + + fn unbind(&self) -> Result<(), &'static str> { + if self.is_current() { + unsafe {wgl::MakeCurrent(ptr::null_mut(),ptr::null_mut() );} + Ok(()) + } + else { + Err("gwl::MakeCurrent (on unbind)") + } + } + + fn handle(&self) -> Self::Handle { + NativeGLContextHandle(self.render_ctx, self.device_ctx) + } +} \ No newline at end of file diff --git a/src/platform/with_wgl/utils.rs b/src/platform/with_wgl/utils.rs new file mode 100644 index 0000000..d49516b --- /dev/null +++ b/src/platform/with_wgl/utils.rs @@ -0,0 +1,692 @@ +use std::mem; +use super::{NativeGLContext, NativeGLContextHandle}; +use super::wgl_attributes::*; + +use std::ffi::{CStr, CString, OsStr}; +use std::os::raw::{c_void, c_int}; +use std::os::windows::ffi::OsStrExt; +use std::io; +use std::ptr; + +use winapi; +use kernel32; +use user32; +use gdi32; +use super::wgl; +use super::wgl_ext; + +pub unsafe fn create_offscreen(shared_with:Option<&NativeGLContextHandle>, settings: &WGLAttributes) + -> Result { + + let window: winapi::HWND = try!(create_hidden_window()); + let hdc = user32::GetDC(window); + if hdc.is_null() { + return Err("GetDC function failed".to_owned()) + } + + let extra = try!(load_extra_functions(window)); + + let extensions = if extra.GetExtensionsStringARB.is_loaded() { + let data = extra.GetExtensionsStringARB(hdc as *const _); + let data = CStr::from_ptr(data).to_bytes().to_vec(); + String::from_utf8(data).unwrap() + + } else if extra.GetExtensionsStringEXT.is_loaded() { + let data = extra.GetExtensionsStringEXT(); + let data = CStr::from_ptr(data).to_bytes().to_vec(); + String::from_utf8(data).unwrap() + + } else { + format!("") + }; + + + let (id, _) = if extensions.split(' ').find(|&i| i == "WGL_ARB_pixel_format").is_some() { + try!(choose_arb_pixel_format(&extra, &extensions, hdc, &settings.pixel_format) + .map_err(|_| "Pixel format not available".to_owned())) + } else { + try!(choose_native_pixel_format(hdc, &settings.pixel_format) + .map_err(|_| "Pixel format not available".to_owned())) + }; + + try!(set_pixel_format(hdc, id)); + + let share = match shared_with { + Some(ref ctx) => ctx.0, + None => ptr::null_mut() + }; + create_full_context(settings, &extra, &extensions, hdc, share) + +} + +//creates a basic context +unsafe fn create_basic_context(hdc: winapi::HDC, share: winapi::HGLRC) + -> Result +{ + let ctx = wgl::CreateContext(hdc as *const c_void); + if ctx.is_null() { + return Err(format!("wglCreateContext failed: {}", + format!("{}", io::Error::last_os_error()))); + } + + if !share.is_null() { + if wgl::ShareLists(share as *const c_void, ctx) == 0 { + return Err(format!("wglShareLists failed: {}", + format!("{}", io::Error::last_os_error()))); + } + }; + + return Ok(NativeGLContext{ + render_ctx : ctx as winapi::HGLRC, + device_ctx: hdc, + weak:false + }); +} + +//creates a full context: attempts to use optional ext WGL functions +unsafe fn create_full_context(settings: &WGLAttributes, extra: &wgl_ext::Wgl, + extensions: &str, hdc: winapi::HDC, share: winapi::HGLRC) + -> Result +{ + if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context").is_none() { + return create_basic_context(hdc, share); + } + + let mut attributes = Vec::new(); + if settings.opengl_es { + if extensions.split(' ').find(|&i| i == "WGL_EXT_create_context_es2_profile").is_some() { + attributes.push(wgl_ext::CONTEXT_PROFILE_MASK_ARB as c_int); + attributes.push(wgl_ext::CONTEXT_ES2_PROFILE_BIT_EXT as c_int); + } else { + return Err("OpenGl Version Not Supported".to_owned()); + } + } + + if settings.major_version > 0 { + attributes.push(wgl_ext::CONTEXT_MAJOR_VERSION_ARB as c_int); + attributes.push(settings.major_version as c_int); + attributes.push(wgl_ext::CONTEXT_MINOR_VERSION_ARB as c_int); + attributes.push(settings.minor_version as c_int); + } + + attributes.push(wgl_ext::CONTEXT_FLAGS_ARB as c_int); + attributes.push((if settings.debug {wgl_ext::CONTEXT_FLAGS_ARB} else {0}) as c_int); + + attributes.push(0); + + let ctx = extra.CreateContextAttribsARB(hdc as *const c_void, + share as *const c_void, + attributes.as_ptr()); + + if ctx.is_null() { + return Err(format!("wglCreateContextAttribsARB failed: {}", + format!("{}", io::Error::last_os_error()))); + } + + //Disable or enable vsync + if extensions.split(' ').find(|&i| i == "WGL_EXT_swap_control").is_some() { + let _guard = try!(CurrentContextGuard::make_current(hdc, ctx as winapi::HGLRC)); + if extra.SwapIntervalEXT(if settings.vsync { 1 } else { 0 }) == 0 { + return Err("wglSwapIntervalEXT failed".to_owned()); + } + } + + return Ok(NativeGLContext{ + render_ctx : ctx as winapi::HGLRC, + device_ctx: hdc, + weak:false + }); +} + +unsafe fn create_hidden_window() -> Result { + + let class_name = register_window_class(); + let mut rect = winapi::RECT { + left: 0, right: 1024 as winapi::LONG, + top: 0, bottom: 768 as winapi::LONG, + }; + let ex_style = winapi::WS_EX_APPWINDOW | winapi::WS_EX_WINDOWEDGE; + let style = winapi::WS_OVERLAPPEDWINDOW | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN; + + user32::AdjustWindowRectEx(&mut rect, style, 0, ex_style); + let title = OsStr::new("WGLwindow").encode_wide().chain(Some(0).into_iter()) + .collect::>(); + + let win = user32::CreateWindowExW(ex_style, class_name.as_ptr(), + title.as_ptr(), style, + winapi::CW_USEDEFAULT, winapi::CW_USEDEFAULT, + rect.right - rect.left, + rect.bottom - rect.top, + ptr::null_mut(), ptr::null_mut(), + kernel32::GetModuleHandleW(ptr::null()), + ptr::null_mut()); + if win.is_null() { + return Err("CreateWindowEx function failed"); + } + + Ok(win) +} + +// *********** +// Utilities to ease WGL context creation: some helper functions taken from glutin +// *********** + +unsafe fn register_window_class() -> Vec { + let class_name = OsStr::new("Window Class").encode_wide().chain(Some(0).into_iter()) + .collect::>(); + + let class = winapi::WNDCLASSEXW { + cbSize: mem::size_of::() as winapi::UINT, + style: winapi::CS_HREDRAW | winapi::CS_VREDRAW | winapi::CS_OWNDC, + lpfnWndProc: Some(proc_callback), + cbClsExtra: 0, + cbWndExtra: 0, + hInstance: kernel32::GetModuleHandleW(ptr::null()), + hIcon: ptr::null_mut(), + hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly + hbrBackground: ptr::null_mut(), + lpszMenuName: ptr::null(), + lpszClassName: class_name.as_ptr(), + hIconSm: ptr::null_mut(), + }; + + // We ignore errors because registering the same window class twice would trigger + // an error, and because errors here are detected during CreateWindowEx anyway. + // Also since there is no weird element in the struct, there is no reason for this + // call to fail. + user32::RegisterClassExW(&class); + + class_name +} + +pub unsafe extern "system" fn proc_callback(window: winapi::HWND, msg: winapi::UINT, + wparam: winapi::WPARAM, lparam: winapi::LPARAM) + -> winapi::LRESULT +{ + match msg { + winapi::WM_PAINT => { + 0 + }, + winapi::WM_ERASEBKGND => { + 0 + }, + _ => { + user32::DefWindowProcW(window, msg, wparam, lparam) + } + } +} + + +// A simple wrapper that destroys the window when it is destroyed. +struct WindowWrapper(winapi::HWND, winapi::HDC); + +impl Drop for WindowWrapper { + #[inline] + fn drop(&mut self) { + unsafe { + user32::DestroyWindow(self.0); + } + } +} + + + +#[derive(Debug, Clone)] +pub struct PixelFormat { + pub hardware_accelerated: bool, + pub color_bits: u8, + pub alpha_bits: u8, + pub depth_bits: u8, + pub stencil_bits: u8, + pub stereoscopy: bool, + pub double_buffer: bool, + pub multisampling: Option, + pub srgb: bool, +} + +unsafe fn choose_arb_pixel_format(extra: &wgl_ext::Wgl, extensions: &str, + hdc: winapi::HDC, reqs: &WGLPixelFormat) + -> Result<(c_int, PixelFormat), ()> +{ + let descriptor = { + let mut out: Vec = Vec::with_capacity(37); + + out.push(wgl_ext::DRAW_TO_WINDOW_ARB as c_int); + out.push(1); + + out.push(wgl_ext::SUPPORT_OPENGL_ARB as c_int); + out.push(1); + + out.push(wgl_ext::PIXEL_TYPE_ARB as c_int); + if reqs.float_color_buffer { + if extensions.split(' ').find(|&i| i == "WGL_ARB_pixel_format_float").is_some() { + out.push(wgl_ext::TYPE_RGBA_FLOAT_ARB as c_int); + } else { + return Err(()); + } + } else { + out.push(wgl_ext::TYPE_RGBA_ARB as c_int); + } + + //Force hardware aceleration + out.push(wgl_ext::ACCELERATION_ARB as c_int); + out.push(wgl_ext::FULL_ACCELERATION_ARB as c_int); + + if let Some(color) = reqs.color_bits { + out.push(wgl_ext::COLOR_BITS_ARB as c_int); + out.push(color as c_int); + } + + if let Some(alpha) = reqs.alpha_bits { + out.push(wgl_ext::ALPHA_BITS_ARB as c_int); + out.push(alpha as c_int); + } + + if let Some(depth) = reqs.depth_bits { + out.push(wgl_ext::DEPTH_BITS_ARB as c_int); + out.push(depth as c_int); + } + + if let Some(stencil) = reqs.stencil_bits { + out.push(wgl_ext::STENCIL_BITS_ARB as c_int); + out.push(stencil as c_int); + } + + // Prefer double buffering if unspecified (probably shouldn't once you can choose) + let double_buffer = reqs.double_buffer.unwrap_or(true); + out.push(wgl_ext::DOUBLE_BUFFER_ARB as c_int); + out.push(if double_buffer { 1 } else { 0 }); + + if let Some(multisampling) = reqs.multisampling { + if extensions.split(' ').find(|&i| i == "WGL_ARB_multisample").is_some() { + out.push(wgl_ext::SAMPLE_BUFFERS_ARB as c_int); + out.push(if multisampling == 0 { 0 } else { 1 }); + out.push(wgl_ext::SAMPLES_ARB as c_int); + out.push(multisampling as c_int); + } else { + return Err(()); + } + } + + out.push(wgl_ext::STEREO_ARB as c_int); + out.push(if reqs.stereoscopy { 1 } else { 0 }); + + if reqs.srgb { + if extensions.split(' ').find(|&i| i == "WGL_ARB_framebuffer_sRGB").is_some() { + out.push(wgl_ext::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int); + out.push(1); + } else if extensions.split(' ').find(|&i| i == "WGL_EXT_framebuffer_sRGB").is_some() { + out.push(wgl_ext::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int); + out.push(1); + } else { + return Err(()); + } + } + + out.push(0); + out + }; + + let mut format_id = mem::uninitialized(); + let mut num_formats = mem::uninitialized(); + if extra.ChoosePixelFormatARB(hdc as *const _, descriptor.as_ptr(), ptr::null(), 1, + &mut format_id, &mut num_formats) == 0 + { + return Err(()); + } + + if num_formats == 0 { + return Err(()); + } + + let get_info = |attrib: u32| { + let mut value = mem::uninitialized(); + extra.GetPixelFormatAttribivARB(hdc as *const _, format_id as c_int, + 0, 1, [attrib as c_int].as_ptr(), + &mut value); + value as u32 + }; + + let pf_desc = PixelFormat { + hardware_accelerated: get_info(wgl_ext::ACCELERATION_ARB) != + wgl_ext::NO_ACCELERATION_ARB, + color_bits: get_info(wgl_ext::RED_BITS_ARB) as u8 + + get_info(wgl_ext::GREEN_BITS_ARB) as u8 + + get_info(wgl_ext::BLUE_BITS_ARB) as u8, + alpha_bits: get_info(wgl_ext::ALPHA_BITS_ARB) as u8, + depth_bits: get_info(wgl_ext::DEPTH_BITS_ARB) as u8, + stencil_bits: get_info(wgl_ext::STENCIL_BITS_ARB) as u8, + stereoscopy: get_info(wgl_ext::STEREO_ARB) != 0, + double_buffer: get_info(wgl_ext::DOUBLE_BUFFER_ARB) != 0, + multisampling: { + if extensions.split(' ').find(|&i| i == "WGL_ARB_multisample").is_some() { + match get_info(wgl_ext::SAMPLES_ARB) { + 0 => None, + a => Some(a as u16), + } + } else { + None + } + }, + srgb: if extensions.split(' ').find(|&i| i == "WGL_ARB_framebuffer_sRGB").is_some() { + get_info(wgl_ext::FRAMEBUFFER_SRGB_CAPABLE_ARB) != 0 + } else if extensions.split(' ').find(|&i| i == "WGL_EXT_framebuffer_sRGB").is_some() { + get_info(wgl_ext::FRAMEBUFFER_SRGB_CAPABLE_EXT) != 0 + } else { + false + }, + }; + + Ok((format_id, pf_desc)) +} + +// Chooses a pixel formats without using WGL. +// +// Gives less precise results than `enumerate_arb_pixel_formats`. +unsafe fn choose_native_pixel_format(hdc: winapi::HDC, reqs: &WGLPixelFormat) + -> Result<(c_int, PixelFormat), ()> +{ + // TODO: hardware acceleration is not handled + + // handling non-supported stuff + if reqs.float_color_buffer { + return Err(()); + } + + match reqs.multisampling { + Some(0) => (), + None => (), + Some(_) => return Err(()) + }; + + if reqs.stereoscopy { + return Err(()); + } + + if reqs.srgb { + return Err(()); + } + + // building the descriptor to pass to ChoosePixelFormat + let descriptor = winapi::PIXELFORMATDESCRIPTOR { + nSize: mem::size_of::() as u16, + nVersion: 1, + dwFlags: { + let f1 = match reqs.double_buffer { + None => winapi::PFD_DOUBLEBUFFER, // Should be PFD_DOUBLEBUFFER_DONTCARE after you can choose + Some(true) => winapi::PFD_DOUBLEBUFFER, + Some(false) => 0, + }; + + let f2 = if reqs.stereoscopy { + winapi::PFD_STEREO + } else { + 0 + }; + + winapi::PFD_DRAW_TO_WINDOW | winapi::PFD_SUPPORT_OPENGL | f1 | f2 + }, + iPixelType: winapi::PFD_TYPE_RGBA, + cColorBits: reqs.color_bits.unwrap_or(0), + cRedBits: 0, + cRedShift: 0, + cGreenBits: 0, + cGreenShift: 0, + cBlueBits: 0, + cBlueShift: 0, + cAlphaBits: reqs.alpha_bits.unwrap_or(0), + cAlphaShift: 0, + cAccumBits: 0, + cAccumRedBits: 0, + cAccumGreenBits: 0, + cAccumBlueBits: 0, + cAccumAlphaBits: 0, + cDepthBits: reqs.depth_bits.unwrap_or(0), + cStencilBits: reqs.stencil_bits.unwrap_or(0), + cAuxBuffers: 0, + iLayerType: winapi::PFD_MAIN_PLANE, + bReserved: 0, + dwLayerMask: 0, + dwVisibleMask: 0, + dwDamageMask: 0, + }; + + // now querying + let pf_id = gdi32::ChoosePixelFormat(hdc, &descriptor); + if pf_id == 0 { + return Err(()); + } + + // querying back the capabilities of what windows told us + let mut output: winapi::PIXELFORMATDESCRIPTOR = mem::zeroed(); + if gdi32::DescribePixelFormat(hdc, pf_id, mem::size_of::() as u32, + &mut output) == 0 + { + return Err(()); + } + + // windows may return us a non-conforming pixel format if none are supported, so we have to + // check this + if (output.dwFlags & winapi::PFD_DRAW_TO_WINDOW) == 0 { + return Err(()); + } + if (output.dwFlags & winapi::PFD_SUPPORT_OPENGL) == 0 { + return Err(()); + } + if output.iPixelType != winapi::PFD_TYPE_RGBA { + return Err(()); + } + + let pf_desc = PixelFormat { + hardware_accelerated: (output.dwFlags & winapi::PFD_GENERIC_FORMAT) == 0, + color_bits: output.cRedBits + output.cGreenBits + output.cBlueBits, + alpha_bits: output.cAlphaBits, + depth_bits: output.cDepthBits, + stencil_bits: output.cStencilBits, + stereoscopy: (output.dwFlags & winapi::PFD_STEREO) != 0, + double_buffer: (output.dwFlags & winapi::PFD_DOUBLEBUFFER) != 0, + multisampling: None, + srgb: false, + }; + + if pf_desc.alpha_bits < reqs.alpha_bits.unwrap_or(0) { + return Err(()); + } + if pf_desc.depth_bits < reqs.depth_bits.unwrap_or(0) { + return Err(()); + } + if pf_desc.stencil_bits < reqs.stencil_bits.unwrap_or(0) { + return Err(()); + } + if pf_desc.color_bits < reqs.color_bits.unwrap_or(0) { + return Err(()); + } + if !pf_desc.hardware_accelerated { + return Err(()); + } + if let Some(req) = reqs.double_buffer { + if pf_desc.double_buffer != req { + return Err(()); + } + } + + Ok((pf_id, pf_desc)) +} + +// Calls `SetPixelFormat` on a window. +unsafe fn set_pixel_format(hdc: winapi::HDC, id: c_int) -> Result<(), String> { + let mut output: winapi::PIXELFORMATDESCRIPTOR = mem::zeroed(); + + if gdi32::DescribePixelFormat(hdc, id, mem::size_of::() + as winapi::UINT, &mut output) == 0 + { + return Err(format!("DescribePixelFormat function failed: {}", + format!("{}", io::Error::last_os_error()))); + } + + if gdi32::SetPixelFormat(hdc, id, &output) == 0 { + return Err(format!("SetPixelFormat function failed: {}", + format!("{}", io::Error::last_os_error()))); + } + + Ok(()) +} + +// Loads the WGL functions that are not guaranteed to be supported. +// +// The `window` must be passed because the driver can vary depending on the window's +// characteristics. +unsafe fn load_extra_functions(window: winapi::HWND) -> Result { + let (ex_style, style) = (winapi::WS_EX_APPWINDOW, winapi::WS_POPUP | + winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN); + + // creating a dummy invisible window + let dummy_window = { + // getting the rect of the real window + let rect = { + let mut placement: winapi::WINDOWPLACEMENT = mem::zeroed(); + placement.length = mem::size_of::() as winapi::UINT; + if user32::GetWindowPlacement(window, &mut placement) == 0 { + panic!(); + } + placement.rcNormalPosition + }; + + // getting the class name of the real window + let mut class_name = [0u16; 128]; + if user32::GetClassNameW(window, class_name.as_mut_ptr(), 128) == 0 { + return Err(format!("GetClassNameW function failed: {}", + format!("{}", io::Error::last_os_error()))); + } + + // this dummy window should match the real one enough to get the same OpenGL driver + let title = OsStr::new("Dummy").encode_wide().chain(Some(0).into_iter()) + .collect::>(); + let win = user32::CreateWindowExW(ex_style, class_name.as_ptr(), + title.as_ptr(), style, + winapi::CW_USEDEFAULT, winapi::CW_USEDEFAULT, + rect.right - rect.left, + rect.bottom - rect.top, + ptr::null_mut(), ptr::null_mut(), + kernel32::GetModuleHandleW(ptr::null()), + ptr::null_mut()); + + if win.is_null() { + return Err(format!("CreateWindowEx function failed: {}", + format!("{}", io::Error::last_os_error()))); + } + + let hdc = user32::GetDC(win); + if hdc.is_null() { + let err = Err(format!("GetDC function failed: {}", + format!("{}", io::Error::last_os_error()))); + return err; + } + + WindowWrapper(win, hdc) + }; + + // getting the pixel format that we will use and setting it + { + let id = try!(choose_dummy_pixel_format(dummy_window.1)); + try!(set_pixel_format(dummy_window.1, id)); + } + + // creating the dummy OpenGL context and making it current + let dummy_context = try!(create_basic_context(dummy_window.1, ptr::null_mut())); + let _current_context = try!(CurrentContextGuard::make_current(dummy_window.1, + dummy_context.render_ctx)); + + // loading the extra WGL functions + Ok(wgl_ext::Wgl::load_with(|addr| { + let addr = CString::new(addr.as_bytes()).unwrap(); + let addr = addr.as_ptr(); + wgl::GetProcAddress(addr) as *const c_void + })) +} + +// This function chooses a pixel format that is likely to be provided by +// the main video driver of the system. +fn choose_dummy_pixel_format(hdc: winapi::HDC) -> Result { + // building the descriptor to pass to ChoosePixelFormat + let descriptor = winapi::PIXELFORMATDESCRIPTOR { + nSize: mem::size_of::() as u16, + nVersion: 1, + dwFlags: winapi::PFD_DRAW_TO_WINDOW | winapi::PFD_SUPPORT_OPENGL | winapi::PFD_DOUBLEBUFFER, + iPixelType: winapi::PFD_TYPE_RGBA, + cColorBits: 24, + cRedBits: 0, + cRedShift: 0, + cGreenBits: 0, + cGreenShift: 0, + cBlueBits: 0, + cBlueShift: 0, + cAlphaBits: 8, + cAlphaShift: 0, + cAccumBits: 0, + cAccumRedBits: 0, + cAccumGreenBits: 0, + cAccumBlueBits: 0, + cAccumAlphaBits: 0, + cDepthBits: 24, + cStencilBits: 8, + cAuxBuffers: 0, + iLayerType: winapi::PFD_MAIN_PLANE, + bReserved: 0, + dwLayerMask: 0, + dwVisibleMask: 0, + dwDamageMask: 0, + }; + + // now querying + let pf_id = unsafe { gdi32::ChoosePixelFormat(hdc, &descriptor) }; + if pf_id == 0 { + return Err("No available pixel format"); + } + + Ok(pf_id) +} + +// A guard for when you want to make the context current. Destroying the guard restores the +// previously-current context. +use std::marker::PhantomData; +pub struct CurrentContextGuard<'a, 'b> { + previous_hdc: winapi::HDC, + previous_hglrc: winapi::HGLRC, + marker1:PhantomData<&'a ()>, + marker2:PhantomData<&'b ()>, +} + +impl<'a, 'b> CurrentContextGuard<'a, 'b> { + pub unsafe fn make_current(hdc: winapi::HDC, context: winapi::HGLRC) + -> Result, String> + { + let previous_hdc = wgl::GetCurrentDC() as winapi::HDC; + let previous_hglrc = wgl::GetCurrentContext() as winapi::HGLRC; + + let result = wgl::MakeCurrent(hdc as *const _, context as *const _); + if result == 0 { + return Err(format!("wglMakeCurrent function failed: {}", + format!("{}", io::Error::last_os_error()))); + } + + Ok(CurrentContextGuard { + previous_hdc: previous_hdc, + previous_hglrc: previous_hglrc, + marker1: PhantomData, + marker2: PhantomData, + }) + } +} + +impl<'a, 'b> Drop for CurrentContextGuard<'a, 'b> { + fn drop(&mut self) { + unsafe { + wgl::MakeCurrent(self.previous_hdc as *const c_void, + self.previous_hglrc as *const c_void); + } + } +} + + + diff --git a/src/platform/with_wgl/wgl_attributes.rs b/src/platform/with_wgl/wgl_attributes.rs new file mode 100644 index 0000000..8c635a3 --- /dev/null +++ b/src/platform/with_wgl/wgl_attributes.rs @@ -0,0 +1,83 @@ +// Attributes to use when creating an OpenGL context. +#[derive(Clone, Debug)] +pub struct WGLAttributes { + // + pub opengl_es: bool, //enable or disable OpenGL ES contest + pub major_version: u32, //OpenGL major version. Set 0 to select the latest + pub minor_version: u32, //OpenGL minor version. + pub debug: bool, //Debug mode improves error information. Disabled by default. + pub vsync: bool, //Enable or disable vsync for swap_buffers. Disabled by default. + pub pixel_format: WGLPixelFormat //Pixel format requirements +} + +impl Default for WGLAttributes { + #[inline] + fn default() -> WGLAttributes { + WGLAttributes { + opengl_es: false, + major_version: 2, + minor_version: 1, + debug: false, + vsync: false, + pixel_format: WGLPixelFormat::default() + } + } +} + +#[derive(Clone, Debug)] +pub struct WGLPixelFormat { + // Minimum number of bits for the color buffer, excluding alpha. `None` means "don't care". + // The default is `Some(24)`. + pub color_bits: Option, + + // If true, the color buffer must be in a floating point format. Default is `false`. + // + // Using floating points allows you to write values outside of the `[0.0, 1.0]` range. + pub float_color_buffer: bool, + + // Minimum number of bits for the alpha in the color buffer. `None` means "don't care". + // The default is `Some(8)`. + pub alpha_bits: Option, + + // Minimum number of bits for the depth buffer. `None` means "don't care". + // The default value is `Some(24)`. + pub depth_bits: Option, + + // Minimum number of bits for the depth buffer. `None` means "don't care". + // The default value is `Some(8)`. + pub stencil_bits: Option, + + // If true, only double-buffered formats will be considered. If false, only single-buffer + // formats. `None` means "don't care". The default is `Some(true)`. + pub double_buffer: Option, + + // Contains the minimum number of samples per pixel in the color, depth and stencil buffers. + // `None` means "don't care". Default is `None`. + // A value of `Some(0)` indicates that multisampling must not be enabled. + pub multisampling: Option, + + // If true, only stereoscopic formats will be considered. If false, only non-stereoscopic + // formats. The default is `false`. + pub stereoscopy: bool, + + // If true, only sRGB-capable formats will be considered. If false, don't care. + // The default is `false`. + pub srgb: bool, +} + +impl Default for WGLPixelFormat { + #[inline] + fn default() -> WGLPixelFormat { + WGLPixelFormat { + color_bits: Some(24), + float_color_buffer: false, + alpha_bits: Some(8), + depth_bits: Some(24), + stencil_bits: Some(8), + double_buffer: None, + multisampling: None, + stereoscopy: false, + srgb: false, + } + } +} \ No newline at end of file From 94d7eaca2f95ece2f66e599eaa466131c19a07bb Mon Sep 17 00:00:00 2001 From: Imanol Fernandez Date: Tue, 20 Sep 2016 16:14:54 +0200 Subject: [PATCH 2/7] Minor fixes and improve rust formatting --- src/platform/with_wgl/mod.rs | 1 - src/platform/with_wgl/native_gl_context.rs | 85 ++++--- src/platform/with_wgl/utils.rs | 283 +++++++++++---------- src/platform/with_wgl/wgl_attributes.rs | 16 +- 4 files changed, 202 insertions(+), 183 deletions(-) diff --git a/src/platform/with_wgl/mod.rs b/src/platform/with_wgl/mod.rs index 2e8cf8b..fb8b1f7 100644 --- a/src/platform/with_wgl/mod.rs +++ b/src/platform/with_wgl/mod.rs @@ -13,4 +13,3 @@ mod native_gl_context; mod utils; pub use self::native_gl_context::NativeGLContext; pub use self::native_gl_context::NativeGLContextHandle; - diff --git a/src/platform/with_wgl/native_gl_context.rs b/src/platform/with_wgl/native_gl_context.rs index 1d8d42d..60fab31 100644 --- a/src/platform/with_wgl/native_gl_context.rs +++ b/src/platform/with_wgl/native_gl_context.rs @@ -1,6 +1,6 @@ use platform::NativeGLContextMethods; use std::ffi::CString; -use std::os::raw::{c_void}; +use std::os::raw::c_void; use std::ptr; use std::sync::{Once, ONCE_INIT}; @@ -19,11 +19,10 @@ lazy_static! { static ref GL_LIB: Option = { let p = unsafe{kernel32::LoadLibraryA(b"opengl32.dll\0".as_ptr() as *const _)}; if p.is_null() { - debug!("opengl32.dll not found!"); + error!("WGL: opengl32.dll not found!"); None - } - else { - debug!("opengl32.dll LOADED!"); + } else { + debug!("WGL: opengl32.dll loaded!"); Some(HMODULEWrapper(p)) } }; @@ -32,16 +31,14 @@ lazy_static! { static LOAD_GL: Once = ONCE_INIT; pub fn load_gl() { LOAD_GL.call_once(|| { - gl::load_with(|s| { - NativeGLContext::get_proc_address(s) as *const _ - }); + gl::load_with(|s| NativeGLContext::get_proc_address(s) as *const _); }); } pub struct NativeGLContext { - pub render_ctx: winapi::HGLRC, - pub device_ctx:winapi::HDC, // - pub weak: bool, + render_ctx: winapi::HGLRC, + device_ctx: winapi::HDC, + weak: bool, } impl Drop for NativeGLContext { @@ -57,30 +54,42 @@ impl Drop for NativeGLContext { } } -pub struct NativeGLContextHandle(pub winapi::HGLRC, pub winapi::HDC); +pub struct NativeGLContextHandle(winapi::HGLRC, winapi::HDC); unsafe impl Send for NativeGLContextHandle {} impl NativeGLContextMethods for NativeGLContext { type Handle = NativeGLContextHandle; fn get_proc_address(addr: &str) -> *const () { - let addr = CString::new(addr.as_bytes()).unwrap().as_ptr(); + let addr = CString::new(addr.as_bytes()).unwrap(); + let addr = addr.as_ptr(); unsafe { let p = wgl::GetProcAddress(addr) as *const _; - if !p.is_null() { return p; } + if !p.is_null() { + return p; + } match *GL_LIB { - Some(ref lib) => kernel32::GetProcAddress(lib.0, addr) as *const _, - None => ptr::null_mut() + Some(ref lib) => kernel32::GetProcAddress(lib.0, addr) as *const _, + None => ptr::null_mut(), } } } fn create_shared(with: Option<&Self::Handle>) -> Result { - match unsafe{super::utils::create_offscreen(with, &WGLAttributes::default())}{ - Ok(ctx) => { - //wglGetProcAddress only works in the presence of a valid GL context - //OpenGL functions must be loaded after the first context is created + let render_ctx = match with { + Some(ref handle) => handle.0, + None => ptr::null_mut(), + }; + match unsafe { super::utils::create_offscreen(render_ctx, &WGLAttributes::default()) } { + Ok(ref res) => { + // wglGetProcAddress only works in the presence of a valid GL context + // OpenGL functions must be loaded after the first context is created + let ctx = NativeGLContext { + render_ctx: res.0, + device_ctx: res.1, + weak: false, + }; ctx.make_current().unwrap(); load_gl(); Ok(ctx) @@ -101,44 +110,44 @@ impl NativeGLContextMethods for NativeGLContext { Some(NativeGLContext { render_ctx: handle.0, device_ctx: handle.1, - weak: true + weak: true, }) - } - else { + } else { None } } fn current_handle() -> Option { - let handle = unsafe{ wgl::GetCurrentContext()}; + let handle = unsafe { wgl::GetCurrentContext() }; if !handle.is_null() { - let hdc = unsafe { wgl::GetCurrentDC()}; + let hdc = unsafe { wgl::GetCurrentDC() }; Some(NativeGLContextHandle(handle as winapi::HGLRC, hdc as winapi::HDC)) - } - else { + } else { None } } fn make_current(&self) -> Result<(), &'static str> { - if unsafe {wgl::MakeCurrent(self.device_ctx as * const _, self.render_ctx as *const _) != 0 } { - Ok(()) - } else { - Err("wgl::makeCurrent failed") + unsafe { + if wgl::MakeCurrent(self.device_ctx as *const _, self.render_ctx as *const _) != 0 { + Ok(()) + } else { + Err("wgl::makeCurrent failed") + } } } fn unbind(&self) -> Result<(), &'static str> { - if self.is_current() { - unsafe {wgl::MakeCurrent(ptr::null_mut(),ptr::null_mut() );} - Ok(()) - } - else { - Err("gwl::MakeCurrent (on unbind)") + unsafe { + if self.is_current() && wgl::MakeCurrent(ptr::null_mut(), ptr::null_mut()) == 0 { + Err("gwl::MakeCurrent (on unbind)") + } else { + Ok(()) + } } } fn handle(&self) -> Self::Handle { NativeGLContextHandle(self.render_ctx, self.device_ctx) } -} \ No newline at end of file +} diff --git a/src/platform/with_wgl/utils.rs b/src/platform/with_wgl/utils.rs index d49516b..5f6e5a0 100644 --- a/src/platform/with_wgl/utils.rs +++ b/src/platform/with_wgl/utils.rs @@ -1,5 +1,4 @@ use std::mem; -use super::{NativeGLContext, NativeGLContextHandle}; use super::wgl_attributes::*; use std::ffi::{CStr, CString, OsStr}; @@ -15,79 +14,74 @@ use gdi32; use super::wgl; use super::wgl_ext; -pub unsafe fn create_offscreen(shared_with:Option<&NativeGLContextHandle>, settings: &WGLAttributes) - -> Result { +pub unsafe fn create_offscreen(shared_with: winapi::HGLRC, + settings: &WGLAttributes) + -> Result<(winapi::HGLRC, winapi::HDC), String> { - let window: winapi::HWND = try!(create_hidden_window()); - let hdc = user32::GetDC(window); - if hdc.is_null() { - return Err("GetDC function failed".to_owned()) - } + let window: winapi::HWND = try!(create_hidden_window()); + let hdc = user32::GetDC(window); + if hdc.is_null() { + return Err("GetDC function failed".to_owned()); + } - let extra = try!(load_extra_functions(window)); + let extra = try!(load_extra_functions(window)); - let extensions = if extra.GetExtensionsStringARB.is_loaded() { - let data = extra.GetExtensionsStringARB(hdc as *const _); - let data = CStr::from_ptr(data).to_bytes().to_vec(); - String::from_utf8(data).unwrap() + let extensions = if extra.GetExtensionsStringARB.is_loaded() { + let data = extra.GetExtensionsStringARB(hdc as *const _); + let data = CStr::from_ptr(data).to_bytes().to_vec(); + String::from_utf8(data).unwrap() - } else if extra.GetExtensionsStringEXT.is_loaded() { - let data = extra.GetExtensionsStringEXT(); - let data = CStr::from_ptr(data).to_bytes().to_vec(); - String::from_utf8(data).unwrap() + } else if extra.GetExtensionsStringEXT.is_loaded() { + let data = extra.GetExtensionsStringEXT(); + let data = CStr::from_ptr(data).to_bytes().to_vec(); + String::from_utf8(data).unwrap() - } else { - format!("") - }; + } else { + format!("") + }; - let (id, _) = if extensions.split(' ').find(|&i| i == "WGL_ARB_pixel_format").is_some() { - try!(choose_arb_pixel_format(&extra, &extensions, hdc, &settings.pixel_format) - .map_err(|_| "Pixel format not available".to_owned())) - } else { - try!(choose_native_pixel_format(hdc, &settings.pixel_format) - .map_err(|_| "Pixel format not available".to_owned())) - }; + let (id, _) = if extensions.split(' ').find(|&i| i == "WGL_ARB_pixel_format").is_some() { + try!(choose_arb_pixel_format(&extra, &extensions, hdc, &settings.pixel_format) + .map_err(|_| "Pixel format not available".to_owned())) + } else { + try!(choose_native_pixel_format(hdc, &settings.pixel_format) + .map_err(|_| "Pixel format not available".to_owned())) + }; - try!(set_pixel_format(hdc, id)); + try!(set_pixel_format(hdc, id)); - let share = match shared_with { - Some(ref ctx) => ctx.0, - None => ptr::null_mut() - }; - create_full_context(settings, &extra, &extensions, hdc, share) + create_full_context(settings, &extra, &extensions, hdc, shared_with) } -//creates a basic context -unsafe fn create_basic_context(hdc: winapi::HDC, share: winapi::HGLRC) - -> Result -{ +// creates a basic context +unsafe fn create_basic_context(hdc: winapi::HDC, + share: winapi::HGLRC) + -> Result<(winapi::HGLRC, winapi::HDC), String> { let ctx = wgl::CreateContext(hdc as *const c_void); if ctx.is_null() { return Err(format!("wglCreateContext failed: {}", - format!("{}", io::Error::last_os_error()))); + format!("{}", io::Error::last_os_error()))); } if !share.is_null() { if wgl::ShareLists(share as *const c_void, ctx) == 0 { return Err(format!("wglShareLists failed: {}", - format!("{}", io::Error::last_os_error()))); + format!("{}", io::Error::last_os_error()))); } }; - return Ok(NativeGLContext{ - render_ctx : ctx as winapi::HGLRC, - device_ctx: hdc, - weak:false - }); + return Ok((ctx as winapi::HGLRC, hdc)); } -//creates a full context: attempts to use optional ext WGL functions -unsafe fn create_full_context(settings: &WGLAttributes, extra: &wgl_ext::Wgl, - extensions: &str, hdc: winapi::HDC, share: winapi::HGLRC) - -> Result -{ +// creates a full context: attempts to use optional ext WGL functions +unsafe fn create_full_context(settings: &WGLAttributes, + extra: &wgl_ext::Wgl, + extensions: &str, + hdc: winapi::HDC, + share: winapi::HGLRC) + -> Result<(winapi::HGLRC, winapi::HDC), String> { if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context").is_none() { return create_basic_context(hdc, share); } @@ -98,7 +92,7 @@ unsafe fn create_full_context(settings: &WGLAttributes, extra: &wgl_ext::Wgl, attributes.push(wgl_ext::CONTEXT_PROFILE_MASK_ARB as c_int); attributes.push(wgl_ext::CONTEXT_ES2_PROFILE_BIT_EXT as c_int); } else { - return Err("OpenGl Version Not Supported".to_owned()); + return Err("OpenGl Version Not Supported".to_owned()); } } @@ -110,56 +104,64 @@ unsafe fn create_full_context(settings: &WGLAttributes, extra: &wgl_ext::Wgl, } attributes.push(wgl_ext::CONTEXT_FLAGS_ARB as c_int); - attributes.push((if settings.debug {wgl_ext::CONTEXT_FLAGS_ARB} else {0}) as c_int); + attributes.push((if settings.debug { + wgl_ext::CONTEXT_FLAGS_ARB + } else { + 0 + }) as c_int); attributes.push(0); let ctx = extra.CreateContextAttribsARB(hdc as *const c_void, - share as *const c_void, - attributes.as_ptr()); + share as *const c_void, + attributes.as_ptr()); if ctx.is_null() { return Err(format!("wglCreateContextAttribsARB failed: {}", - format!("{}", io::Error::last_os_error()))); + format!("{}", io::Error::last_os_error()))); } - //Disable or enable vsync + // Disable or enable vsync if extensions.split(' ').find(|&i| i == "WGL_EXT_swap_control").is_some() { let _guard = try!(CurrentContextGuard::make_current(hdc, ctx as winapi::HGLRC)); if extra.SwapIntervalEXT(if settings.vsync { 1 } else { 0 }) == 0 { return Err("wglSwapIntervalEXT failed".to_owned()); } } - - return Ok(NativeGLContext{ - render_ctx : ctx as winapi::HGLRC, - device_ctx: hdc, - weak:false - }); + + return Ok((ctx as winapi::HGLRC, hdc)); } unsafe fn create_hidden_window() -> Result { let class_name = register_window_class(); let mut rect = winapi::RECT { - left: 0, right: 1024 as winapi::LONG, - top: 0, bottom: 768 as winapi::LONG, + left: 0, + right: 1024 as winapi::LONG, + top: 0, + bottom: 768 as winapi::LONG, }; let ex_style = winapi::WS_EX_APPWINDOW | winapi::WS_EX_WINDOWEDGE; let style = winapi::WS_OVERLAPPEDWINDOW | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN; user32::AdjustWindowRectEx(&mut rect, style, 0, ex_style); - let title = OsStr::new("WGLwindow").encode_wide().chain(Some(0).into_iter()) - .collect::>(); - - let win = user32::CreateWindowExW(ex_style, class_name.as_ptr(), - title.as_ptr(), style, - winapi::CW_USEDEFAULT, winapi::CW_USEDEFAULT, - rect.right - rect.left, - rect.bottom - rect.top, - ptr::null_mut(), ptr::null_mut(), - kernel32::GetModuleHandleW(ptr::null()), - ptr::null_mut()); + let title = OsStr::new("WGLwindow") + .encode_wide() + .chain(Some(0).into_iter()) + .collect::>(); + + let win = user32::CreateWindowExW(ex_style, + class_name.as_ptr(), + title.as_ptr(), + style, + winapi::CW_USEDEFAULT, + winapi::CW_USEDEFAULT, + rect.right - rect.left, + rect.bottom - rect.top, + ptr::null_mut(), + ptr::null_mut(), + kernel32::GetModuleHandleW(ptr::null()), + ptr::null_mut()); if win.is_null() { return Err("CreateWindowEx function failed"); } @@ -172,8 +174,10 @@ unsafe fn create_hidden_window() -> Result { // *********** unsafe fn register_window_class() -> Vec { - let class_name = OsStr::new("Window Class").encode_wide().chain(Some(0).into_iter()) - .collect::>(); + let class_name = OsStr::new("Window Class") + .encode_wide() + .chain(Some(0).into_iter()) + .collect::>(); let class = winapi::WNDCLASSEXW { cbSize: mem::size_of::() as winapi::UINT, @@ -183,7 +187,7 @@ unsafe fn register_window_class() -> Vec { cbWndExtra: 0, hInstance: kernel32::GetModuleHandleW(ptr::null()), hIcon: ptr::null_mut(), - hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly + hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly hbrBackground: ptr::null_mut(), lpszMenuName: ptr::null(), lpszClassName: class_name.as_ptr(), @@ -199,20 +203,15 @@ unsafe fn register_window_class() -> Vec { class_name } -pub unsafe extern "system" fn proc_callback(window: winapi::HWND, msg: winapi::UINT, - wparam: winapi::WPARAM, lparam: winapi::LPARAM) - -> winapi::LRESULT -{ +pub unsafe extern "system" fn proc_callback(window: winapi::HWND, + msg: winapi::UINT, + wparam: winapi::WPARAM, + lparam: winapi::LPARAM) + -> winapi::LRESULT { match msg { - winapi::WM_PAINT => { - 0 - }, - winapi::WM_ERASEBKGND => { - 0 - }, - _ => { - user32::DefWindowProcW(window, msg, wparam, lparam) - } + winapi::WM_PAINT => 0, + winapi::WM_ERASEBKGND => 0, + _ => user32::DefWindowProcW(window, msg, wparam, lparam), } } @@ -244,10 +243,11 @@ pub struct PixelFormat { pub srgb: bool, } -unsafe fn choose_arb_pixel_format(extra: &wgl_ext::Wgl, extensions: &str, - hdc: winapi::HDC, reqs: &WGLPixelFormat) - -> Result<(c_int, PixelFormat), ()> -{ +unsafe fn choose_arb_pixel_format(extra: &wgl_ext::Wgl, + extensions: &str, + hdc: winapi::HDC, + reqs: &WGLPixelFormat) + -> Result<(c_int, PixelFormat), ()> { let descriptor = { let mut out: Vec = Vec::with_capacity(37); @@ -268,7 +268,7 @@ unsafe fn choose_arb_pixel_format(extra: &wgl_ext::Wgl, extensions: &str, out.push(wgl_ext::TYPE_RGBA_ARB as c_int); } - //Force hardware aceleration + // Force hardware aceleration out.push(wgl_ext::ACCELERATION_ARB as c_int); out.push(wgl_ext::FULL_ACCELERATION_ARB as c_int); @@ -329,9 +329,12 @@ unsafe fn choose_arb_pixel_format(extra: &wgl_ext::Wgl, extensions: &str, let mut format_id = mem::uninitialized(); let mut num_formats = mem::uninitialized(); - if extra.ChoosePixelFormatARB(hdc as *const _, descriptor.as_ptr(), ptr::null(), 1, - &mut format_id, &mut num_formats) == 0 - { + if extra.ChoosePixelFormatARB(hdc as *const _, + descriptor.as_ptr(), + ptr::null(), + 1, + &mut format_id, + &mut num_formats) == 0 { return Err(()); } @@ -341,16 +344,18 @@ unsafe fn choose_arb_pixel_format(extra: &wgl_ext::Wgl, extensions: &str, let get_info = |attrib: u32| { let mut value = mem::uninitialized(); - extra.GetPixelFormatAttribivARB(hdc as *const _, format_id as c_int, - 0, 1, [attrib as c_int].as_ptr(), + extra.GetPixelFormatAttribivARB(hdc as *const _, + format_id as c_int, + 0, + 1, + [attrib as c_int].as_ptr(), &mut value); value as u32 }; let pf_desc = PixelFormat { - hardware_accelerated: get_info(wgl_ext::ACCELERATION_ARB) != - wgl_ext::NO_ACCELERATION_ARB, - color_bits: get_info(wgl_ext::RED_BITS_ARB) as u8 + + hardware_accelerated: get_info(wgl_ext::ACCELERATION_ARB) != wgl_ext::NO_ACCELERATION_ARB, + color_bits: get_info(wgl_ext::RED_BITS_ARB) as u8 + get_info(wgl_ext::GREEN_BITS_ARB) as u8 + get_info(wgl_ext::BLUE_BITS_ARB) as u8, alpha_bits: get_info(wgl_ext::ALPHA_BITS_ARB) as u8, @@ -370,7 +375,9 @@ unsafe fn choose_arb_pixel_format(extra: &wgl_ext::Wgl, extensions: &str, }, srgb: if extensions.split(' ').find(|&i| i == "WGL_ARB_framebuffer_sRGB").is_some() { get_info(wgl_ext::FRAMEBUFFER_SRGB_CAPABLE_ARB) != 0 - } else if extensions.split(' ').find(|&i| i == "WGL_EXT_framebuffer_sRGB").is_some() { + } else if extensions.split(' ') + .find(|&i| i == "WGL_EXT_framebuffer_sRGB") + .is_some() { get_info(wgl_ext::FRAMEBUFFER_SRGB_CAPABLE_EXT) != 0 } else { false @@ -383,9 +390,9 @@ unsafe fn choose_arb_pixel_format(extra: &wgl_ext::Wgl, extensions: &str, // Chooses a pixel formats without using WGL. // // Gives less precise results than `enumerate_arb_pixel_formats`. -unsafe fn choose_native_pixel_format(hdc: winapi::HDC, reqs: &WGLPixelFormat) - -> Result<(c_int, PixelFormat), ()> -{ +unsafe fn choose_native_pixel_format(hdc: winapi::HDC, + reqs: &WGLPixelFormat) + -> Result<(c_int, PixelFormat), ()> { // TODO: hardware acceleration is not handled // handling non-supported stuff @@ -396,7 +403,7 @@ unsafe fn choose_native_pixel_format(hdc: winapi::HDC, reqs: &WGLPixelFormat) match reqs.multisampling { Some(0) => (), None => (), - Some(_) => return Err(()) + Some(_) => return Err(()), }; if reqs.stereoscopy { @@ -413,7 +420,8 @@ unsafe fn choose_native_pixel_format(hdc: winapi::HDC, reqs: &WGLPixelFormat) nVersion: 1, dwFlags: { let f1 = match reqs.double_buffer { - None => winapi::PFD_DOUBLEBUFFER, // Should be PFD_DOUBLEBUFFER_DONTCARE after you can choose + // Should be PFD_DOUBLEBUFFER_DONTCARE after you can choose + None => winapi::PFD_DOUBLEBUFFER, Some(true) => winapi::PFD_DOUBLEBUFFER, Some(false) => 0, }; @@ -459,9 +467,10 @@ unsafe fn choose_native_pixel_format(hdc: winapi::HDC, reqs: &WGLPixelFormat) // querying back the capabilities of what windows told us let mut output: winapi::PIXELFORMATDESCRIPTOR = mem::zeroed(); - if gdi32::DescribePixelFormat(hdc, pf_id, mem::size_of::() as u32, - &mut output) == 0 - { + if gdi32::DescribePixelFormat(hdc, + pf_id, + mem::size_of::() as u32, + &mut output) == 0 { return Err(()); } @@ -526,7 +535,7 @@ unsafe fn set_pixel_format(hdc: winapi::HDC, id: c_int) -> Result<(), String> { if gdi32::SetPixelFormat(hdc, id, &output) == 0 { return Err(format!("SetPixelFormat function failed: {}", - format!("{}", io::Error::last_os_error()))); + format!("{}", io::Error::last_os_error()))); } Ok(()) @@ -537,8 +546,8 @@ unsafe fn set_pixel_format(hdc: winapi::HDC, id: c_int) -> Result<(), String> { // The `window` must be passed because the driver can vary depending on the window's // characteristics. unsafe fn load_extra_functions(window: winapi::HWND) -> Result { - let (ex_style, style) = (winapi::WS_EX_APPWINDOW, winapi::WS_POPUP | - winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN); + let (ex_style, style) = (winapi::WS_EX_APPWINDOW, + winapi::WS_POPUP | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN); // creating a dummy invisible window let dummy_window = { @@ -556,30 +565,36 @@ unsafe fn load_extra_functions(window: winapi::HWND) -> Result>(); - let win = user32::CreateWindowExW(ex_style, class_name.as_ptr(), - title.as_ptr(), style, - winapi::CW_USEDEFAULT, winapi::CW_USEDEFAULT, + let title = OsStr::new("Dummy") + .encode_wide() + .chain(Some(0).into_iter()) + .collect::>(); + let win = user32::CreateWindowExW(ex_style, + class_name.as_ptr(), + title.as_ptr(), + style, + winapi::CW_USEDEFAULT, + winapi::CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top, - ptr::null_mut(), ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), kernel32::GetModuleHandleW(ptr::null()), ptr::null_mut()); if win.is_null() { return Err(format!("CreateWindowEx function failed: {}", - format!("{}", io::Error::last_os_error()))); + format!("{}", io::Error::last_os_error()))); } let hdc = user32::GetDC(win); if hdc.is_null() { let err = Err(format!("GetDC function failed: {}", - format!("{}", io::Error::last_os_error()))); + format!("{}", io::Error::last_os_error()))); return err; } @@ -594,8 +609,7 @@ unsafe fn load_extra_functions(window: winapi::HWND) -> Result { previous_hdc: winapi::HDC, previous_hglrc: winapi::HGLRC, - marker1:PhantomData<&'a ()>, - marker2:PhantomData<&'b ()>, + marker1: PhantomData<&'a ()>, + marker2: PhantomData<&'b ()>, } impl<'a, 'b> CurrentContextGuard<'a, 'b> { - pub unsafe fn make_current(hdc: winapi::HDC, context: winapi::HGLRC) - -> Result, String> - { + pub unsafe fn make_current(hdc: winapi::HDC, + context: winapi::HGLRC) + -> Result, String> { let previous_hdc = wgl::GetCurrentDC() as winapi::HDC; let previous_hglrc = wgl::GetCurrentContext() as winapi::HGLRC; let result = wgl::MakeCurrent(hdc as *const _, context as *const _); if result == 0 { return Err(format!("wglMakeCurrent function failed: {}", - format!("{}", io::Error::last_os_error()))); + format!("{}", io::Error::last_os_error()))); } Ok(CurrentContextGuard { @@ -683,10 +697,7 @@ impl<'a, 'b> Drop for CurrentContextGuard<'a, 'b> { fn drop(&mut self) { unsafe { wgl::MakeCurrent(self.previous_hdc as *const c_void, - self.previous_hglrc as *const c_void); + self.previous_hglrc as *const c_void); } } } - - - diff --git a/src/platform/with_wgl/wgl_attributes.rs b/src/platform/with_wgl/wgl_attributes.rs index 8c635a3..e291b37 100644 --- a/src/platform/with_wgl/wgl_attributes.rs +++ b/src/platform/with_wgl/wgl_attributes.rs @@ -2,12 +2,12 @@ #[derive(Clone, Debug)] pub struct WGLAttributes { // - pub opengl_es: bool, //enable or disable OpenGL ES contest - pub major_version: u32, //OpenGL major version. Set 0 to select the latest - pub minor_version: u32, //OpenGL minor version. - pub debug: bool, //Debug mode improves error information. Disabled by default. - pub vsync: bool, //Enable or disable vsync for swap_buffers. Disabled by default. - pub pixel_format: WGLPixelFormat //Pixel format requirements + pub opengl_es: bool, // enable or disable OpenGL ES contest + pub major_version: u32, // OpenGL major version. Set 0 to select the latest + pub minor_version: u32, // OpenGL minor version. + pub debug: bool, // Debug mode improves error information. Disabled by default. + pub vsync: bool, // Enable or disable vsync for swap_buffers. Disabled by default. + pub pixel_format: WGLPixelFormat, // Pixel format requirements } impl Default for WGLAttributes { @@ -19,7 +19,7 @@ impl Default for WGLAttributes { minor_version: 1, debug: false, vsync: false, - pixel_format: WGLPixelFormat::default() + pixel_format: WGLPixelFormat::default(), } } } @@ -80,4 +80,4 @@ impl Default for WGLPixelFormat { srgb: false, } } -} \ No newline at end of file +} From 1cb36fe383a938ab95dc2543d1e90c3668f3496f Mon Sep 17 00:00:00 2001 From: Imanol Fernandez Date: Wed, 21 Sep 2016 14:26:56 +0200 Subject: [PATCH 3/7] Add attributions to the glutin code and format functions --- src/platform/with_wgl/utils.rs | 40 +++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/platform/with_wgl/utils.rs b/src/platform/with_wgl/utils.rs index 5f6e5a0..4963f8f 100644 --- a/src/platform/with_wgl/utils.rs +++ b/src/platform/with_wgl/utils.rs @@ -14,6 +14,14 @@ use gdi32; use super::wgl; use super::wgl_ext; + +// #Attributions +// This WGL implementation has been inspired by the code originating in Glutin. +// We have used slightly modified version util functions to manage WGL contexts. +// We simplified the win32 window creation because we don't need the event handler thread +// We'd like to credit and thank all of the Glutin contributors for their work. +// (https://github.com/tomaka/glutin) + pub unsafe fn create_offscreen(shared_with: winapi::HGLRC, settings: &WGLAttributes) -> Result<(winapi::HGLRC, winapi::HDC), String> { @@ -37,7 +45,7 @@ pub unsafe fn create_offscreen(shared_with: winapi::HGLRC, String::from_utf8(data).unwrap() } else { - format!("") + String::new() }; @@ -61,18 +69,16 @@ unsafe fn create_basic_context(hdc: winapi::HDC, -> Result<(winapi::HGLRC, winapi::HDC), String> { let ctx = wgl::CreateContext(hdc as *const c_void); if ctx.is_null() { - return Err(format!("wglCreateContext failed: {}", - format!("{}", io::Error::last_os_error()))); + return Err(format!("wglCreateContext failed: {}", io::Error::last_os_error())); } if !share.is_null() { if wgl::ShareLists(share as *const c_void, ctx) == 0 { - return Err(format!("wglShareLists failed: {}", - format!("{}", io::Error::last_os_error()))); + return Err(format!("wglShareLists failed: {}", io::Error::last_os_error())); } }; - return Ok((ctx as winapi::HGLRC, hdc)); + Ok((ctx as winapi::HGLRC, hdc)) } // creates a full context: attempts to use optional ext WGL functions @@ -118,7 +124,7 @@ unsafe fn create_full_context(settings: &WGLAttributes, if ctx.is_null() { return Err(format!("wglCreateContextAttribsARB failed: {}", - format!("{}", io::Error::last_os_error()))); + io::Error::last_os_error())); } // Disable or enable vsync @@ -129,7 +135,7 @@ unsafe fn create_full_context(settings: &WGLAttributes, } } - return Ok((ctx as winapi::HGLRC, hdc)); + Ok((ctx as winapi::HGLRC, hdc)) } unsafe fn create_hidden_window() -> Result { @@ -170,7 +176,9 @@ unsafe fn create_hidden_window() -> Result { } // *********** -// Utilities to ease WGL context creation: some helper functions taken from glutin +// Utilities to ease WGL context creation +// Slightly modified versions of util functions taken from Glutin +// (https://github.com/tomaka/glutin) // *********** unsafe fn register_window_class() -> Vec { @@ -529,13 +537,12 @@ unsafe fn set_pixel_format(hdc: winapi::HDC, id: c_int) -> Result<(), String> { if gdi32::DescribePixelFormat(hdc, id, mem::size_of::() as winapi::UINT, &mut output) == 0 { - return Err(format!("DescribePixelFormat function failed: {}", - format!("{}", io::Error::last_os_error()))); + return Err(format!("DescribePixelFormat function failed: {}",io::Error::last_os_error())); } if gdi32::SetPixelFormat(hdc, id, &output) == 0 { return Err(format!("SetPixelFormat function failed: {}", - format!("{}", io::Error::last_os_error()))); + io::Error::last_os_error())); } Ok(()) @@ -565,7 +572,7 @@ unsafe fn load_extra_functions(window: winapi::HWND) -> Result Result CurrentContextGuard<'a, 'b> { let result = wgl::MakeCurrent(hdc as *const _, context as *const _); if result == 0 { return Err(format!("wglMakeCurrent function failed: {}", - format!("{}", io::Error::last_os_error()))); + io::Error::last_os_error())); } Ok(CurrentContextGuard { From a94952008471fceab5ee00b47c6296e44c04ae8f Mon Sep 17 00:00:00 2001 From: Imanol Fernandez Date: Wed, 21 Sep 2016 14:27:08 +0200 Subject: [PATCH 4/7] Check for null HDC value --- src/platform/with_wgl/native_gl_context.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/platform/with_wgl/native_gl_context.rs b/src/platform/with_wgl/native_gl_context.rs index 60fab31..05b1fde 100644 --- a/src/platform/with_wgl/native_gl_context.rs +++ b/src/platform/with_wgl/native_gl_context.rs @@ -118,12 +118,12 @@ impl NativeGLContextMethods for NativeGLContext { } fn current_handle() -> Option { - let handle = unsafe { wgl::GetCurrentContext() }; - if !handle.is_null() { - let hdc = unsafe { wgl::GetCurrentDC() }; - Some(NativeGLContextHandle(handle as winapi::HGLRC, hdc as winapi::HDC)) - } else { + let ctx = unsafe { wgl::GetCurrentContext() }; + let hdc = unsafe { wgl::GetCurrentDC() }; + if ctx.is_null() || hdc.is_null() { None + } else { + Some(NativeGLContextHandle(ctx as winapi::HGLRC, hdc as winapi::HDC)) } } From ca5fce102694c5e048b98107f99b4afb2b56297b Mon Sep 17 00:00:00 2001 From: Imanol Fernandez Date: Wed, 21 Sep 2016 14:27:18 +0200 Subject: [PATCH 5/7] Use a dummy context for wglGetProcAddress when needed instead of loading gl symbols after a context creation: wglGetProcAddress only works in the presence of a valid GL context, so we use a dummy ctx when the caller calls wglGetProcAddress without a valid GL context bound --- src/platform/with_wgl/native_gl_context.rs | 56 ++++++++++++++++------ 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/src/platform/with_wgl/native_gl_context.rs b/src/platform/with_wgl/native_gl_context.rs index 05b1fde..8ebfe87 100644 --- a/src/platform/with_wgl/native_gl_context.rs +++ b/src/platform/with_wgl/native_gl_context.rs @@ -2,22 +2,21 @@ use platform::NativeGLContextMethods; use std::ffi::CString; use std::os::raw::c_void; use std::ptr; -use std::sync::{Once, ONCE_INIT}; use winapi; use user32; use kernel32; use super::wgl; use super::wgl_attributes::*; -use gleam::gl; +use super::utils; -/// Wrapper to satisfy `Sync`. +// Wrappers to satisfy `Sync`. struct HMODULEWrapper(winapi::HMODULE); unsafe impl Sync for HMODULEWrapper {} lazy_static! { static ref GL_LIB: Option = { - let p = unsafe{kernel32::LoadLibraryA(b"opengl32.dll\0".as_ptr() as *const _)}; + let p = unsafe { kernel32::LoadLibraryA(b"opengl32.dll\0".as_ptr() as *const _) }; if p.is_null() { error!("WGL: opengl32.dll not found!"); None @@ -26,13 +25,23 @@ lazy_static! { Some(HMODULEWrapper(p)) } }; -} -static LOAD_GL: Once = ONCE_INIT; -pub fn load_gl() { - LOAD_GL.call_once(|| { - gl::load_with(|s| NativeGLContext::get_proc_address(s) as *const _); - }); + static ref PROC_ADDR_CTX: Option = { + match unsafe { utils::create_offscreen(ptr::null_mut(), &WGLAttributes::default()) } { + Ok(ref res) => { + let ctx = NativeGLContext { + render_ctx: res.0, + device_ctx: res.1, + weak: false, + }; + Some(ctx) + } + Err(s) => { + error!("Error creating GetProcAddress helper context: {}", s); + None + } + } + }; } pub struct NativeGLContext { @@ -54,8 +63,12 @@ impl Drop for NativeGLContext { } } +unsafe impl Send for NativeGLContext {} +unsafe impl Sync for NativeGLContext {} + pub struct NativeGLContextHandle(winapi::HGLRC, winapi::HDC); unsafe impl Send for NativeGLContextHandle {} +unsafe impl Sync for NativeGLContextHandle {} impl NativeGLContextMethods for NativeGLContext { type Handle = NativeGLContextHandle; @@ -64,10 +77,27 @@ impl NativeGLContextMethods for NativeGLContext { let addr = CString::new(addr.as_bytes()).unwrap(); let addr = addr.as_ptr(); unsafe { + + if wgl::GetCurrentContext().is_null() { + // wglGetProcAddress only works in the presence of a valid GL context + // We use a dummy ctx when the caller calls this function without a valid GL context + if let Some(ref ctx) = *PROC_ADDR_CTX { + if ctx.make_current().is_err() { + return ptr::null_mut(); + } + } else { + return ptr::null_mut(); + } + } + let p = wgl::GetProcAddress(addr) as *const _; if !p.is_null() { return p; } + // wglGetProcAddress​ doesn't return function pointers for some legacy functions, + // (the ones coming from OpenGL 1.1) + // These functions are exported by the opengl32.dll itself, + // so we have to fallback to kernel32 getProcAddress if wglGetProcAddress​ return null match *GL_LIB { Some(ref lib) => kernel32::GetProcAddress(lib.0, addr) as *const _, None => ptr::null_mut(), @@ -81,17 +111,15 @@ impl NativeGLContextMethods for NativeGLContext { Some(ref handle) => handle.0, None => ptr::null_mut(), }; - match unsafe { super::utils::create_offscreen(render_ctx, &WGLAttributes::default()) } { + match unsafe { utils::create_offscreen(render_ctx, &WGLAttributes::default()) } { Ok(ref res) => { - // wglGetProcAddress only works in the presence of a valid GL context - // OpenGL functions must be loaded after the first context is created + let ctx = NativeGLContext { render_ctx: res.0, device_ctx: res.1, weak: false, }; ctx.make_current().unwrap(); - load_gl(); Ok(ctx) } Err(s) => { From 46fda297af6cd35c0445b36f6dad3b9890c098ba Mon Sep 17 00:00:00 2001 From: Imanol Fernandez Date: Tue, 27 Sep 2016 18:46:59 +0200 Subject: [PATCH 6/7] Remove unneeded wglMakeCurrent & minor fixes --- src/platform/with_wgl/native_gl_context.rs | 6 ++---- src/platform/with_wgl/utils.rs | 12 +++++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/platform/with_wgl/native_gl_context.rs b/src/platform/with_wgl/native_gl_context.rs index 8ebfe87..d9f8c04 100644 --- a/src/platform/with_wgl/native_gl_context.rs +++ b/src/platform/with_wgl/native_gl_context.rs @@ -113,13 +113,11 @@ impl NativeGLContextMethods for NativeGLContext { }; match unsafe { utils::create_offscreen(render_ctx, &WGLAttributes::default()) } { Ok(ref res) => { - let ctx = NativeGLContext { render_ctx: res.0, device_ctx: res.1, weak: false, }; - ctx.make_current().unwrap(); Ok(ctx) } Err(s) => { @@ -160,7 +158,7 @@ impl NativeGLContextMethods for NativeGLContext { if wgl::MakeCurrent(self.device_ctx as *const _, self.render_ctx as *const _) != 0 { Ok(()) } else { - Err("wgl::makeCurrent failed") + Err("WGL::makeCurrent failed") } } } @@ -168,7 +166,7 @@ impl NativeGLContextMethods for NativeGLContext { fn unbind(&self) -> Result<(), &'static str> { unsafe { if self.is_current() && wgl::MakeCurrent(ptr::null_mut(), ptr::null_mut()) == 0 { - Err("gwl::MakeCurrent (on unbind)") + Err("WGL::MakeCurrent (on unbind)") } else { Ok(()) } diff --git a/src/platform/with_wgl/utils.rs b/src/platform/with_wgl/utils.rs index 4963f8f..3c88ef3 100644 --- a/src/platform/with_wgl/utils.rs +++ b/src/platform/with_wgl/utils.rs @@ -88,13 +88,14 @@ unsafe fn create_full_context(settings: &WGLAttributes, hdc: winapi::HDC, share: winapi::HGLRC) -> Result<(winapi::HGLRC, winapi::HDC), String> { - if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context").is_none() { + let mut extensions = extensions.split(' '); + if extensions.find(|&i| i == "WGL_ARB_create_context").is_none() { return create_basic_context(hdc, share); } let mut attributes = Vec::new(); if settings.opengl_es { - if extensions.split(' ').find(|&i| i == "WGL_EXT_create_context_es2_profile").is_some() { + if extensions.find(|&i| i == "WGL_EXT_create_context_es2_profile").is_some() { attributes.push(wgl_ext::CONTEXT_PROFILE_MASK_ARB as c_int); attributes.push(wgl_ext::CONTEXT_ES2_PROFILE_BIT_EXT as c_int); } else { @@ -127,9 +128,11 @@ unsafe fn create_full_context(settings: &WGLAttributes, io::Error::last_os_error())); } + if wgl::MakeCurrent(hdc as *const _, ctx as *const _) == 0 { + return Err("wglMakeCurrent failed".to_owned()); + } // Disable or enable vsync - if extensions.split(' ').find(|&i| i == "WGL_EXT_swap_control").is_some() { - let _guard = try!(CurrentContextGuard::make_current(hdc, ctx as winapi::HGLRC)); + if extensions.find(|&i| i == "WGL_EXT_swap_control").is_some() { if extra.SwapIntervalEXT(if settings.vsync { 1 } else { 0 }) == 0 { return Err("wglSwapIntervalEXT failed".to_owned()); } @@ -139,7 +142,6 @@ unsafe fn create_full_context(settings: &WGLAttributes, } unsafe fn create_hidden_window() -> Result { - let class_name = register_window_class(); let mut rect = winapi::RECT { left: 0, From 0876fc35dfc1cbfe546f445b40f18bb46c688274 Mon Sep 17 00:00:00 2001 From: Imanol Fernandez Date: Wed, 28 Sep 2016 20:39:08 +0200 Subject: [PATCH 7/7] Improve disposed winapi & WGL resources in error paths --- src/platform/with_wgl/native_gl_context.rs | 5 +- src/platform/with_wgl/utils.rs | 215 +++++++++++---------- src/platform/with_wgl/wgl_attributes.rs | 3 +- 3 files changed, 112 insertions(+), 111 deletions(-) diff --git a/src/platform/with_wgl/native_gl_context.rs b/src/platform/with_wgl/native_gl_context.rs index d9f8c04..399b269 100644 --- a/src/platform/with_wgl/native_gl_context.rs +++ b/src/platform/with_wgl/native_gl_context.rs @@ -54,8 +54,11 @@ impl Drop for NativeGLContext { fn drop(&mut self) { unsafe { if !self.weak { + // the context to be deleted needs to be unbound + self.unbind().unwrap(); wgl::DeleteContext(self.render_ctx as *const _); let window = user32::WindowFromDC(self.device_ctx); + debug_assert!(!window.is_null()); user32::ReleaseDC(window, self.device_ctx); user32::DestroyWindow(window); } @@ -77,7 +80,6 @@ impl NativeGLContextMethods for NativeGLContext { let addr = CString::new(addr.as_bytes()).unwrap(); let addr = addr.as_ptr(); unsafe { - if wgl::GetCurrentContext().is_null() { // wglGetProcAddress only works in the presence of a valid GL context // We use a dummy ctx when the caller calls this function without a valid GL context @@ -103,7 +105,6 @@ impl NativeGLContextMethods for NativeGLContext { None => ptr::null_mut(), } } - } fn create_shared(with: Option<&Self::Handle>) -> Result { diff --git a/src/platform/with_wgl/utils.rs b/src/platform/with_wgl/utils.rs index 3c88ef3..81fbe20 100644 --- a/src/platform/with_wgl/utils.rs +++ b/src/platform/with_wgl/utils.rs @@ -25,60 +25,63 @@ use super::wgl_ext; pub unsafe fn create_offscreen(shared_with: winapi::HGLRC, settings: &WGLAttributes) -> Result<(winapi::HGLRC, winapi::HDC), String> { - - let window: winapi::HWND = try!(create_hidden_window()); - let hdc = user32::GetDC(window); - if hdc.is_null() { + let mut ctx = WGLScopedContext::default(); + ctx.window = try!(create_hidden_window()); + ctx.device_ctx = user32::GetDC(ctx.window); + if ctx.device_ctx.is_null() { return Err("GetDC function failed".to_owned()); } - let extra = try!(load_extra_functions(window)); + let extra = try!(load_extra_functions(ctx.window)); let extensions = if extra.GetExtensionsStringARB.is_loaded() { - let data = extra.GetExtensionsStringARB(hdc as *const _); + let data = extra.GetExtensionsStringARB(ctx.device_ctx as *const _); let data = CStr::from_ptr(data).to_bytes().to_vec(); String::from_utf8(data).unwrap() - } else if extra.GetExtensionsStringEXT.is_loaded() { let data = extra.GetExtensionsStringEXT(); let data = CStr::from_ptr(data).to_bytes().to_vec(); String::from_utf8(data).unwrap() - } else { String::new() }; - let (id, _) = if extensions.split(' ').find(|&i| i == "WGL_ARB_pixel_format").is_some() { - try!(choose_arb_pixel_format(&extra, &extensions, hdc, &settings.pixel_format) - .map_err(|_| "Pixel format not available".to_owned())) + try!(choose_arb_pixel_format(&extra, &extensions, ctx.device_ctx, &settings.pixel_format) + .map_err(|_| "ARB pixel format not available".to_owned())) } else { - try!(choose_native_pixel_format(hdc, &settings.pixel_format) - .map_err(|_| "Pixel format not available".to_owned())) + try!(choose_native_pixel_format(ctx.device_ctx, &settings.pixel_format) + .map_err(|_| "Native pixel format not available".to_owned())) }; - try!(set_pixel_format(hdc, id)); - - create_full_context(settings, &extra, &extensions, hdc, shared_with) + try!(set_pixel_format(ctx.device_ctx, id)); + let result = create_full_context(settings, &extra, &extensions, ctx.device_ctx, shared_with); + if result.is_ok() { + mem::forget(ctx); // Everything is ok, don't dispose the scoped ctx + } + result } // creates a basic context unsafe fn create_basic_context(hdc: winapi::HDC, share: winapi::HGLRC) -> Result<(winapi::HGLRC, winapi::HDC), String> { - let ctx = wgl::CreateContext(hdc as *const c_void); - if ctx.is_null() { + let mut ctx = WGLScopedContext::default(); + ctx.render_ctx = wgl::CreateContext(hdc as *const _) as winapi::HGLRC; + if ctx.render_ctx.is_null() { return Err(format!("wglCreateContext failed: {}", io::Error::last_os_error())); } if !share.is_null() { - if wgl::ShareLists(share as *const c_void, ctx) == 0 { + if wgl::ShareLists(share as *const _, ctx.render_ctx as *const _) == 0 { return Err(format!("wglShareLists failed: {}", io::Error::last_os_error())); } }; - Ok((ctx as winapi::HGLRC, hdc)) + let result = (ctx.render_ctx, hdc); + mem::forget(ctx); // Everything is ok, don't dispose the scoped ctx + Ok(result) } // creates a full context: attempts to use optional ext WGL functions @@ -90,7 +93,13 @@ unsafe fn create_full_context(settings: &WGLAttributes, -> Result<(winapi::HGLRC, winapi::HDC), String> { let mut extensions = extensions.split(' '); if extensions.find(|&i| i == "WGL_ARB_create_context").is_none() { - return create_basic_context(hdc, share); + let ctx = create_basic_context(hdc, share); + if let Ok(ctx) = ctx { + if wgl::MakeCurrent(ctx.1 as *const _, ctx.0 as *const _) == 0 { + return Err("wglMakeCurrent failed creating a basic context".to_owned()); + } + } + return ctx; } let mut attributes = Vec::new(); @@ -119,17 +128,18 @@ unsafe fn create_full_context(settings: &WGLAttributes, attributes.push(0); - let ctx = extra.CreateContextAttribsARB(hdc as *const c_void, - share as *const c_void, - attributes.as_ptr()); + let mut ctx = WGLScopedContext::default(); + ctx.render_ctx = extra.CreateContextAttribsARB(hdc as *const _, + share as *const _, + attributes.as_ptr()) as winapi::HGLRC; - if ctx.is_null() { + if ctx.render_ctx.is_null() { return Err(format!("wglCreateContextAttribsARB failed: {}", io::Error::last_os_error())); } - if wgl::MakeCurrent(hdc as *const _, ctx as *const _) == 0 { - return Err("wglMakeCurrent failed".to_owned()); + if wgl::MakeCurrent(hdc as *const _, ctx.render_ctx as *const _) == 0 { + return Err("wglMakeCurrent failed creating full context".to_owned()); } // Disable or enable vsync if extensions.find(|&i| i == "WGL_EXT_swap_control").is_some() { @@ -137,8 +147,9 @@ unsafe fn create_full_context(settings: &WGLAttributes, return Err("wglSwapIntervalEXT failed".to_owned()); } } - - Ok((ctx as winapi::HGLRC, hdc)) + let result = (ctx.render_ctx as winapi::HGLRC, hdc); + mem::forget(ctx); + Ok(result) } unsafe fn create_hidden_window() -> Result { @@ -177,6 +188,50 @@ unsafe fn create_hidden_window() -> Result { Ok(win) } +// Helper struct used to dispose resources in error paths +struct WGLScopedContext { + pub window: winapi::HWND, + pub device_ctx: winapi::HDC, + pub render_ctx: winapi::HGLRC, +} + +impl Drop for WGLScopedContext { + fn drop(&mut self) { + unsafe { + if !self.render_ctx.is_null() { + wgl::DeleteContext(self.render_ctx as *const _); + } + if !self.device_ctx.is_null() && !self.window.is_null() { + user32::ReleaseDC(self.window, self.device_ctx); + } + if !self.window.is_null() { + user32::DestroyWindow(self.window); + } + } + } +} + +impl Default for WGLScopedContext { + #[inline] + fn default() -> WGLScopedContext { + WGLScopedContext { + window: ptr::null_mut(), + device_ctx: ptr::null_mut(), + render_ctx: ptr::null_mut(), + } + } +} + +impl WGLScopedContext { + fn new(window: winapi::HWND, device_ctx: winapi::HDC, render_ctx: winapi::HGLRC) -> WGLScopedContext { + WGLScopedContext { + window: window, + device_ctx: device_ctx, + render_ctx: render_ctx, + } + } +} + // *********** // Utilities to ease WGL context creation // Slightly modified versions of util functions taken from Glutin @@ -225,21 +280,6 @@ pub unsafe extern "system" fn proc_callback(window: winapi::HWND, } } - -// A simple wrapper that destroys the window when it is destroyed. -struct WindowWrapper(winapi::HWND, winapi::HDC); - -impl Drop for WindowWrapper { - #[inline] - fn drop(&mut self) { - unsafe { - user32::DestroyWindow(self.0); - } - } -} - - - #[derive(Debug, Clone)] pub struct PixelFormat { pub hardware_accelerated: bool, @@ -398,21 +438,17 @@ unsafe fn choose_arb_pixel_format(extra: &wgl_ext::Wgl, } // Chooses a pixel formats without using WGL. -// // Gives less precise results than `enumerate_arb_pixel_formats`. unsafe fn choose_native_pixel_format(hdc: winapi::HDC, reqs: &WGLPixelFormat) -> Result<(c_int, PixelFormat), ()> { - // TODO: hardware acceleration is not handled - // handling non-supported stuff if reqs.float_color_buffer { return Err(()); } match reqs.multisampling { - Some(0) => (), - None => (), + Some(0) | None => (), Some(_) => return Err(()), }; @@ -537,8 +573,7 @@ unsafe fn set_pixel_format(hdc: winapi::HDC, id: c_int) -> Result<(), String> { let mut output: winapi::PIXELFORMATDESCRIPTOR = mem::zeroed(); if gdi32::DescribePixelFormat(hdc, id, mem::size_of::() - as winapi::UINT, &mut output) == 0 - { + as winapi::UINT, &mut output) == 0 { return Err(format!("DescribePixelFormat function failed: {}",io::Error::last_os_error())); } @@ -557,15 +592,14 @@ unsafe fn set_pixel_format(hdc: winapi::HDC, id: c_int) -> Result<(), String> { unsafe fn load_extra_functions(window: winapi::HWND) -> Result { let (ex_style, style) = (winapi::WS_EX_APPWINDOW, winapi::WS_POPUP | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN); - // creating a dummy invisible window - let dummy_window = { + let mut dummy_window = { // getting the rect of the real window let rect = { let mut placement: winapi::WINDOWPLACEMENT = mem::zeroed(); placement.length = mem::size_of::() as winapi::UINT; if user32::GetWindowPlacement(window, &mut placement) == 0 { - panic!(); + return Err("user32::GetWindowPlacement failed".to_owned()); } placement.rcNormalPosition }; @@ -596,35 +630,43 @@ unsafe fn load_extra_functions(window: winapi::HWND) -> Result Result { } Ok(pf_id) -} - -// A guard for when you want to make the context current. Destroying the guard restores the -// previously-current context. -use std::marker::PhantomData; -pub struct CurrentContextGuard<'a, 'b> { - previous_hdc: winapi::HDC, - previous_hglrc: winapi::HGLRC, - marker1: PhantomData<&'a ()>, - marker2: PhantomData<&'b ()>, -} - -impl<'a, 'b> CurrentContextGuard<'a, 'b> { - pub unsafe fn make_current(hdc: winapi::HDC, - context: winapi::HGLRC) - -> Result, String> { - let previous_hdc = wgl::GetCurrentDC() as winapi::HDC; - let previous_hglrc = wgl::GetCurrentContext() as winapi::HGLRC; - - let result = wgl::MakeCurrent(hdc as *const _, context as *const _); - if result == 0 { - return Err(format!("wglMakeCurrent function failed: {}", - io::Error::last_os_error())); - } - - Ok(CurrentContextGuard { - previous_hdc: previous_hdc, - previous_hglrc: previous_hglrc, - marker1: PhantomData, - marker2: PhantomData, - }) - } -} - -impl<'a, 'b> Drop for CurrentContextGuard<'a, 'b> { - fn drop(&mut self) { - unsafe { - wgl::MakeCurrent(self.previous_hdc as *const c_void, - self.previous_hglrc as *const c_void); - } - } -} +} \ No newline at end of file diff --git a/src/platform/with_wgl/wgl_attributes.rs b/src/platform/with_wgl/wgl_attributes.rs index e291b37..19834e2 100644 --- a/src/platform/with_wgl/wgl_attributes.rs +++ b/src/platform/with_wgl/wgl_attributes.rs @@ -1,7 +1,6 @@ // Attributes to use when creating an OpenGL context. #[derive(Clone, Debug)] pub struct WGLAttributes { - // pub opengl_es: bool, // enable or disable OpenGL ES contest pub major_version: u32, // OpenGL major version. Set 0 to select the latest pub minor_version: u32, // OpenGL minor version. @@ -80,4 +79,4 @@ impl Default for WGLPixelFormat { srgb: false, } } -} +} \ No newline at end of file