Skip to content

Commit dc5868f

Browse files
committed
Improve disposed winapi & WGL resources in error paths
1 parent 46fda29 commit dc5868f

File tree

3 files changed

+116
-111
lines changed

3 files changed

+116
-111
lines changed

src/platform/with_wgl/native_gl_context.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,13 @@ impl Drop for NativeGLContext {
5454
fn drop(&mut self) {
5555
unsafe {
5656
if !self.weak {
57+
self.unbind().unwrap(); //wglDeleteContext requires unbind
5758
wgl::DeleteContext(self.render_ctx as *const _);
5859
let window = user32::WindowFromDC(self.device_ctx);
59-
user32::ReleaseDC(window, self.device_ctx);
60-
user32::DestroyWindow(window);
60+
if !window.is_null() {
61+
user32::ReleaseDC(window, self.device_ctx);
62+
user32::DestroyWindow(window);
63+
}
6164
}
6265
}
6366
}
@@ -77,7 +80,6 @@ impl NativeGLContextMethods for NativeGLContext {
7780
let addr = CString::new(addr.as_bytes()).unwrap();
7881
let addr = addr.as_ptr();
7982
unsafe {
80-
8183
if wgl::GetCurrentContext().is_null() {
8284
// wglGetProcAddress only works in the presence of a valid GL context
8385
// We use a dummy ctx when the caller calls this function without a valid GL context
@@ -103,7 +105,6 @@ impl NativeGLContextMethods for NativeGLContext {
103105
None => ptr::null_mut(),
104106
}
105107
}
106-
107108
}
108109

109110
fn create_shared(with: Option<&Self::Handle>) -> Result<NativeGLContext, &'static str> {

src/platform/with_wgl/utils.rs

Lines changed: 110 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -25,60 +25,61 @@ use super::wgl_ext;
2525
pub unsafe fn create_offscreen(shared_with: winapi::HGLRC,
2626
settings: &WGLAttributes)
2727
-> Result<(winapi::HGLRC, winapi::HDC), String> {
28-
29-
let window: winapi::HWND = try!(create_hidden_window());
30-
let hdc = user32::GetDC(window);
31-
if hdc.is_null() {
28+
let mut ctx = WGLScopedContext::default();
29+
ctx.window = try!(create_hidden_window());
30+
ctx.device_ctx = user32::GetDC(ctx.window);
31+
if ctx.device_ctx.is_null() {
3232
return Err("GetDC function failed".to_owned());
3333
}
3434

35-
let extra = try!(load_extra_functions(window));
35+
let extra = try!(load_extra_functions(ctx.window));
3636

3737
let extensions = if extra.GetExtensionsStringARB.is_loaded() {
38-
let data = extra.GetExtensionsStringARB(hdc as *const _);
38+
let data = extra.GetExtensionsStringARB(ctx.device_ctx as *const _);
3939
let data = CStr::from_ptr(data).to_bytes().to_vec();
4040
String::from_utf8(data).unwrap()
41-
4241
} else if extra.GetExtensionsStringEXT.is_loaded() {
4342
let data = extra.GetExtensionsStringEXT();
4443
let data = CStr::from_ptr(data).to_bytes().to_vec();
4544
String::from_utf8(data).unwrap()
46-
4745
} else {
4846
String::new()
4947
};
5048

51-
5249
let (id, _) = if extensions.split(' ').find(|&i| i == "WGL_ARB_pixel_format").is_some() {
53-
try!(choose_arb_pixel_format(&extra, &extensions, hdc, &settings.pixel_format)
54-
.map_err(|_| "Pixel format not available".to_owned()))
50+
try!(choose_arb_pixel_format(&extra, &extensions, ctx.device_ctx, &settings.pixel_format)
51+
.map_err(|_| "ARB pixel format not available".to_owned()))
5552
} else {
56-
try!(choose_native_pixel_format(hdc, &settings.pixel_format)
57-
.map_err(|_| "Pixel format not available".to_owned()))
53+
try!(choose_native_pixel_format(ctx.device_ctx, &settings.pixel_format)
54+
.map_err(|_| "Native pixel format not available".to_owned()))
5855
};
5956

60-
try!(set_pixel_format(hdc, id));
61-
62-
create_full_context(settings, &extra, &extensions, hdc, shared_with)
57+
try!(set_pixel_format(ctx.device_ctx, id));
6358

59+
let result = create_full_context(settings, &extra, &extensions, ctx.device_ctx, shared_with);
60+
if result.is_ok() {
61+
ctx.dispose = false; // Everything is ok, don't dispose the scoped ctx
62+
}
63+
result
6464
}
6565

6666
// creates a basic context
6767
unsafe fn create_basic_context(hdc: winapi::HDC,
6868
share: winapi::HGLRC)
6969
-> Result<(winapi::HGLRC, winapi::HDC), String> {
70-
let ctx = wgl::CreateContext(hdc as *const c_void);
71-
if ctx.is_null() {
70+
let mut ctx = WGLScopedContext::default();
71+
ctx.render_ctx = wgl::CreateContext(hdc as *const _) as winapi::HGLRC;
72+
if ctx.render_ctx.is_null() {
7273
return Err(format!("wglCreateContext failed: {}", io::Error::last_os_error()));
7374
}
7475

7576
if !share.is_null() {
76-
if wgl::ShareLists(share as *const c_void, ctx) == 0 {
77+
if wgl::ShareLists(share as *const _, ctx.render_ctx as *const _) == 0 {
7778
return Err(format!("wglShareLists failed: {}", io::Error::last_os_error()));
7879
}
7980
};
80-
81-
Ok((ctx as winapi::HGLRC, hdc))
81+
ctx.dispose = false;
82+
Ok((ctx.render_ctx, hdc))
8283
}
8384

8485
// creates a full context: attempts to use optional ext WGL functions
@@ -90,7 +91,13 @@ unsafe fn create_full_context(settings: &WGLAttributes,
9091
-> Result<(winapi::HGLRC, winapi::HDC), String> {
9192
let mut extensions = extensions.split(' ');
9293
if extensions.find(|&i| i == "WGL_ARB_create_context").is_none() {
93-
return create_basic_context(hdc, share);
94+
let ctx = create_basic_context(hdc, share);
95+
if let Ok(ctx) = ctx {
96+
if wgl::MakeCurrent(ctx.1 as *const _, ctx.0 as *const _) == 0 {
97+
return Err("wglMakeCurrent failed creating a basic context".to_owned());
98+
}
99+
}
100+
return ctx;
94101
}
95102

96103
let mut attributes = Vec::new();
@@ -119,26 +126,27 @@ unsafe fn create_full_context(settings: &WGLAttributes,
119126

120127
attributes.push(0);
121128

122-
let ctx = extra.CreateContextAttribsARB(hdc as *const c_void,
123-
share as *const c_void,
124-
attributes.as_ptr());
129+
let mut ctx = WGLScopedContext::default();
130+
ctx.render_ctx = extra.CreateContextAttribsARB(hdc as *const _,
131+
share as *const _,
132+
attributes.as_ptr()) as winapi::HGLRC;
125133

126-
if ctx.is_null() {
134+
if ctx.render_ctx.is_null() {
127135
return Err(format!("wglCreateContextAttribsARB failed: {}",
128136
io::Error::last_os_error()));
129137
}
130138

131-
if wgl::MakeCurrent(hdc as *const _, ctx as *const _) == 0 {
132-
return Err("wglMakeCurrent failed".to_owned());
139+
if wgl::MakeCurrent(hdc as *const _, ctx.render_ctx as *const _) == 0 {
140+
return Err("wglMakeCurrent failed creating full context".to_owned());
133141
}
134142
// Disable or enable vsync
135143
if extensions.find(|&i| i == "WGL_EXT_swap_control").is_some() {
136144
if extra.SwapIntervalEXT(if settings.vsync { 1 } else { 0 }) == 0 {
137145
return Err("wglSwapIntervalEXT failed".to_owned());
138146
}
139147
}
140-
141-
Ok((ctx as winapi::HGLRC, hdc))
148+
ctx.dispose = false;
149+
Ok((ctx.render_ctx as winapi::HGLRC, hdc))
142150
}
143151

144152
unsafe fn create_hidden_window() -> Result<winapi::HWND, &'static str> {
@@ -177,6 +185,55 @@ unsafe fn create_hidden_window() -> Result<winapi::HWND, &'static str> {
177185
Ok(win)
178186
}
179187

188+
// Helper struct used to dispose resources in error paths
189+
struct WGLScopedContext {
190+
pub window: winapi::HWND,
191+
pub device_ctx: winapi::HDC,
192+
pub render_ctx: winapi::HGLRC,
193+
pub dispose: bool
194+
}
195+
196+
impl Drop for WGLScopedContext {
197+
fn drop(&mut self) {
198+
unsafe {
199+
if self.dispose {
200+
if self.render_ctx.is_null() {
201+
wgl::DeleteContext(self.render_ctx as *const _);
202+
}
203+
if !self.device_ctx.is_null() && !self.window.is_null() {
204+
user32::ReleaseDC(self.window, self.device_ctx);
205+
}
206+
if !self.window.is_null() {
207+
user32::DestroyWindow(self.window);
208+
}
209+
}
210+
}
211+
}
212+
}
213+
214+
impl Default for WGLScopedContext {
215+
#[inline]
216+
fn default() -> WGLScopedContext {
217+
WGLScopedContext {
218+
window: ptr::null_mut(),
219+
device_ctx: ptr::null_mut(),
220+
render_ctx: ptr::null_mut(),
221+
dispose: true
222+
}
223+
}
224+
}
225+
226+
impl WGLScopedContext {
227+
fn new(window: winapi::HWND, device_ctx: winapi::HDC, render_ctx: winapi::HGLRC) -> WGLScopedContext {
228+
WGLScopedContext {
229+
window: window,
230+
device_ctx: device_ctx,
231+
render_ctx: render_ctx,
232+
dispose: true
233+
}
234+
}
235+
}
236+
180237
// ***********
181238
// Utilities to ease WGL context creation
182239
// Slightly modified versions of util functions taken from Glutin
@@ -225,21 +282,6 @@ pub unsafe extern "system" fn proc_callback(window: winapi::HWND,
225282
}
226283
}
227284

228-
229-
// A simple wrapper that destroys the window when it is destroyed.
230-
struct WindowWrapper(winapi::HWND, winapi::HDC);
231-
232-
impl Drop for WindowWrapper {
233-
#[inline]
234-
fn drop(&mut self) {
235-
unsafe {
236-
user32::DestroyWindow(self.0);
237-
}
238-
}
239-
}
240-
241-
242-
243285
#[derive(Debug, Clone)]
244286
pub struct PixelFormat {
245287
pub hardware_accelerated: bool,
@@ -398,21 +440,17 @@ unsafe fn choose_arb_pixel_format(extra: &wgl_ext::Wgl,
398440
}
399441

400442
// Chooses a pixel formats without using WGL.
401-
//
402443
// Gives less precise results than `enumerate_arb_pixel_formats`.
403444
unsafe fn choose_native_pixel_format(hdc: winapi::HDC,
404445
reqs: &WGLPixelFormat)
405446
-> Result<(c_int, PixelFormat), ()> {
406-
// TODO: hardware acceleration is not handled
407-
408447
// handling non-supported stuff
409448
if reqs.float_color_buffer {
410449
return Err(());
411450
}
412451

413452
match reqs.multisampling {
414-
Some(0) => (),
415-
None => (),
453+
Some(0) | None => (),
416454
Some(_) => return Err(()),
417455
};
418456

@@ -559,13 +597,13 @@ unsafe fn load_extra_functions(window: winapi::HWND) -> Result<wgl_ext::Wgl, Str
559597
winapi::WS_POPUP | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN);
560598

561599
// creating a dummy invisible window
562-
let dummy_window = {
600+
let mut dummy_window = {
563601
// getting the rect of the real window
564602
let rect = {
565603
let mut placement: winapi::WINDOWPLACEMENT = mem::zeroed();
566604
placement.length = mem::size_of::<winapi::WINDOWPLACEMENT>() as winapi::UINT;
567605
if user32::GetWindowPlacement(window, &mut placement) == 0 {
568-
panic!();
606+
return Err("user32::GetWindowPlacement failed".to_owned());
569607
}
570608
placement.rcNormalPosition
571609
};
@@ -596,35 +634,43 @@ unsafe fn load_extra_functions(window: winapi::HWND) -> Result<wgl_ext::Wgl, Str
596634
ptr::null_mut());
597635

598636
if win.is_null() {
599-
return Err(format!("CreateWindowEx function failed: {}",
600-
io::Error::last_os_error()));
637+
return Err(format!("CreateWindowEx function failed: {}", io::Error::last_os_error()));
601638
}
602639

603640
let hdc = user32::GetDC(win);
641+
let ctx = WGLScopedContext::new(win, hdc, ptr::null_mut());
642+
604643
if hdc.is_null() {
605-
let err = Err(format!("GetDC function failed: {}", io::Error::last_os_error()));
606-
return err;
644+
return Err(format!("GetDC function failed: {}", io::Error::last_os_error()));
607645
}
608646

609-
WindowWrapper(win, hdc)
647+
ctx
610648
};
611649

612650
// getting the pixel format that we will use and setting it
613651
{
614-
let id = try!(choose_dummy_pixel_format(dummy_window.1));
615-
try!(set_pixel_format(dummy_window.1, id));
652+
let id = try!(choose_dummy_pixel_format(dummy_window.device_ctx));
653+
try!(set_pixel_format(dummy_window.device_ctx, id));
616654
}
617655

618656
// creating the dummy OpenGL context and making it current
619-
let dummy_context = try!(create_basic_context(dummy_window.1, ptr::null_mut()));
620-
let _current_context = try!(CurrentContextGuard::make_current(dummy_window.1, dummy_context.0));
657+
dummy_window.render_ctx = try!(create_basic_context(dummy_window.device_ctx, ptr::null_mut())).0;
658+
if wgl::MakeCurrent(dummy_window.device_ctx as *const _, dummy_window.render_ctx as *const _) == 0 {
659+
return Err("WGL::MakeCurrent failed before loading extra WGL functions".to_owned());
660+
}
621661

622662
// loading the extra WGL functions
623-
Ok(wgl_ext::Wgl::load_with(|addr| {
663+
let result = wgl_ext::Wgl::load_with(|addr| {
624664
let addr = CString::new(addr.as_bytes()).unwrap();
625665
let addr = addr.as_ptr();
626666
wgl::GetProcAddress(addr) as *const c_void
627-
}))
667+
});
668+
669+
// Unbind the current context so WGLScopedContext can release resources properly
670+
// wgl::DeleteContex must be called with unbound contexts
671+
wgl::MakeCurrent(ptr::null_mut(), ptr::null_mut());
672+
673+
Ok(result)
628674
}
629675

630676
// This function chooses a pixel format that is likely to be provided by
@@ -667,45 +713,4 @@ fn choose_dummy_pixel_format(hdc: winapi::HDC) -> Result<c_int, &'static str> {
667713
}
668714

669715
Ok(pf_id)
670-
}
671-
672-
// A guard for when you want to make the context current. Destroying the guard restores the
673-
// previously-current context.
674-
use std::marker::PhantomData;
675-
pub struct CurrentContextGuard<'a, 'b> {
676-
previous_hdc: winapi::HDC,
677-
previous_hglrc: winapi::HGLRC,
678-
marker1: PhantomData<&'a ()>,
679-
marker2: PhantomData<&'b ()>,
680-
}
681-
682-
impl<'a, 'b> CurrentContextGuard<'a, 'b> {
683-
pub unsafe fn make_current(hdc: winapi::HDC,
684-
context: winapi::HGLRC)
685-
-> Result<CurrentContextGuard<'a, 'b>, String> {
686-
let previous_hdc = wgl::GetCurrentDC() as winapi::HDC;
687-
let previous_hglrc = wgl::GetCurrentContext() as winapi::HGLRC;
688-
689-
let result = wgl::MakeCurrent(hdc as *const _, context as *const _);
690-
if result == 0 {
691-
return Err(format!("wglMakeCurrent function failed: {}",
692-
io::Error::last_os_error()));
693-
}
694-
695-
Ok(CurrentContextGuard {
696-
previous_hdc: previous_hdc,
697-
previous_hglrc: previous_hglrc,
698-
marker1: PhantomData,
699-
marker2: PhantomData,
700-
})
701-
}
702-
}
703-
704-
impl<'a, 'b> Drop for CurrentContextGuard<'a, 'b> {
705-
fn drop(&mut self) {
706-
unsafe {
707-
wgl::MakeCurrent(self.previous_hdc as *const c_void,
708-
self.previous_hglrc as *const c_void);
709-
}
710-
}
711-
}
716+
}

0 commit comments

Comments
 (0)