-
Notifications
You must be signed in to change notification settings - Fork 86
Implement WGL #64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement WGL #64
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super exciting to see this!
I need to run and I still haven't made it to the end. A lot of formatting nits and a few questions, but I still need to reach to the core of this PR, I'll try to do it over the rest of this week.
Thanks!
static ref GL_LIB: Option<HMODULEWrapper> = { | ||
let p = unsafe{kernel32::LoadLibraryA(b"opengl32.dll\0".as_ptr() as *const _)}; | ||
if p.is_null() { | ||
debug!("opengl32.dll not found!"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: error!
here will make it appear on the console, and seems more likely what we want.
|
||
lazy_static! { | ||
static ref GL_LIB: Option<HMODULEWrapper> = { | ||
let p = unsafe{kernel32::LoadLibraryA(b"opengl32.dll\0".as_ptr() as *const _)}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: unsafe {
, and as *const _) };
.
|
||
pub struct NativeGLContext { | ||
pub render_ctx: winapi::HGLRC, | ||
pub device_ctx:winapi::HDC, // |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: space after the colon. Also, either do comment, or remove the two slashes.
Please make the members private, since the intention is making NativeGLContext
cross-platform. Feel free to add accessor methods if needed.
} | ||
} | ||
|
||
pub struct NativeGLContextHandle(pub winapi::HGLRC, pub winapi::HDC); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here regarding pub
.
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 _, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm... I'm worried we can end up mixing symbols from taken from GL_LIB
and from wgl. May it be a problem?
If it is, please only call wgl::GetProcAddress
when it's None
, if it's not, please write a comment saying why :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems strange but we have to load and mix symbols from both because wglGetProcAddress doesn't return function pointers for some legacy functions that are exported by the opengl32.dll itself (old methods coming from OpenGL 1.1). I'll add some comments to the code to explain this.
} | ||
|
||
fn create_shared(with: Option<&Self::Handle>) -> Result<NativeGLContext, &'static str> { | ||
match unsafe{super::utils::create_offscreen(with, &WGLAttributes::default())}{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: spaces between braces, just as before.
None => ptr::null_mut() | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: extra newline
} | ||
|
||
fn current_handle() -> Option<Self::Handle> { | ||
let handle = unsafe{ wgl::GetCurrentContext()}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: spacing in braces, here and in the rest of the file.
device_ctx: handle.1, | ||
weak: true | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: rust style is } else {
.
Ok(()) | ||
} | ||
else { | ||
Err("gwl::MakeCurrent (on unbind)") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We definitely shouldn't return an error if the current context was already unbound, the error condition should be wgl::MakeCurrent
failing.
(Sorry, misclick) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need a bit more study still, but this looks great! Let me know if I can help somehow :)
fn current_handle() -> Option<Self::Handle> { | ||
let handle = unsafe { wgl::GetCurrentContext() }; | ||
if !handle.is_null() { | ||
let hdc = unsafe { wgl::GetCurrentDC() }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we assert!
here that hdc
is not null (or return an 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()))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this second format!
(here and above) shouldn't be needed.
} | ||
}; | ||
|
||
return Ok((ctx as winapi::HGLRC, hdc)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: no need the return
keyword at the end of the function, you can avoid the semicolon at the end of the line.
} | ||
|
||
// creates a full context: attempts to use optional ext WGL functions | ||
unsafe fn create_full_context(settings: &WGLAttributes, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems sensible enough, though, I'll need to study a bit more since I'm not confident enough with the windows API. Thanks for pointing to glutin.
Also, can you add proper attribution to the glutin
code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added more attribution to glutin code and link to their repo in the last commit. Maybe you could create a licence/author files in this repo and we can add more attribution there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code looks reasonable, I can't test it, but I'm happy with it. Please rebase, do a squash and then I'll merge it.
If you find a way to run the tests this using AppVeyor or similar, that'd be awesome :-)
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)") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: wgl
instead of gwl
.
|
||
// 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)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure you need the CurrentContextGuard
here? It seems to me we're going to bind the context right away after this, so we can rather bind it directly.
} | ||
|
||
unsafe fn create_hidden_window() -> Result<winapi::HWND, &'static str> { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Remove this extra line.
…ing 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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I hadn't look more carefully the error paths, I've left a few comments on that to prevent leaks.
let addr = CString::new(addr.as_bytes()).unwrap(); | ||
let addr = addr.as_ptr(); | ||
unsafe { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: extra line.
None => ptr::null_mut(), | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Here too
let mut placement: winapi::WINDOWPLACEMENT = mem::zeroed(); | ||
placement.length = mem::size_of::<winapi::WINDOWPLACEMENT>() as winapi::UINT; | ||
if user32::GetWindowPlacement(window, &mut placement) == 0 { | ||
panic!(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please panic with a descriptive message, or return an error.
|
||
let hdc = user32::GetDC(win); | ||
if hdc.is_null() { | ||
let err = Err(format!("GetDC function failed: {}", io::Error::last_os_error())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: return Err(..)
. Also, it seems you need to clean up the window you just created.
io::Error::last_os_error())); | ||
} | ||
|
||
let hdc = user32::GetDC(win); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume hdc
doesn't need any cleanup, am I right?
.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())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice using two different errors here.
} | ||
|
||
match reqs.multisampling { | ||
Some(0) => (), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Some(0) | None => ()
, instead of two braces.
|
||
// querying back the capabilities of what windows told us | ||
let mut output: winapi::PIXELFORMATDESCRIPTOR = mem::zeroed(); | ||
if gdi32::DescribePixelFormat(hdc, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does pf_id
need cleanup?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, its just a pixel format index and doesn't require a release call
// Attributes to use when creating an OpenGL context. | ||
#[derive(Clone, Debug)] | ||
pub struct WGLAttributes { | ||
// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Empty comment, please remove.
} | ||
|
||
if !share.is_null() { | ||
if wgl::ShareLists(share as *const c_void, ctx) == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to clean up the context if this fails.
I have improved WGL & winapi disposal in error paths using a scoped struct. Also got rid of CurrentContextGuard code. |
}); | ||
|
||
// Unbind the current context so WGLScopedContext can release resources properly | ||
// wgl::DeleteContex must be called with unbound contexts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: DeleteContext
|
||
let result = create_full_context(settings, &extra, &extensions, ctx.device_ctx, shared_with); | ||
if result.is_ok() { | ||
ctx.dispose = false; // Everything is ok, don't dispose the scoped ctx |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably something like removing the dispose
flag, and use mem::forget
is better here? It's probably fine anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the mem::forget approach, it seems more rusty
@@ -54,10 +54,13 @@ impl Drop for NativeGLContext { | |||
fn drop(&mut self) { | |||
unsafe { | |||
if !self.weak { | |||
self.unbind().unwrap(); //wglDeleteContext requires unbind |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mean that the context to be deleted needs to be unbound? Or that there must be no current context.
I guess this is the first one, but please be clearer in this regard in the comment. Also, space after //
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improved comments
wgl::DeleteContext(self.render_ctx as *const _); | ||
let window = user32::WindowFromDC(self.device_ctx); | ||
user32::ReleaseDC(window, self.device_ctx); | ||
user32::DestroyWindow(window); | ||
if !window.is_null() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what has suddenly making window
nullable? If it happened before and is something you've fixed, I guess that's fine.
But if it's because we can create multiple contexts associated with the same device context, and then have the window deleted, that can mean multiple things if I'm correct:
- The context doesn't require the window to stay alive, in which case we can delete it earlier.
- The context does require the window to stay alive, in which case this is wrong.
- The context does require the window to stay alive at least until we do sharing? That doesn't make sense to me but could be it.
Can you tell me which one is the relevant one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are not creating multiple contexts associated with the same HDC. It was just a extra safety check because null is a possible return value of WindowFromDC. It shouldn't happen in our code, but it seems safer to check for null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we instead assert that it's not null, and handle it only if under some circumstances the assertion fails?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, let's do it. I don't think I can say too much right now with my limited wgl/winapi experience, and this has already stayed here for really long.
Sorry it took so long @MortimerGoro! Hopefully I wasn't too nitpicky :)
Thanks for this work!
If you look into how to test this (with AppVeyor, for example), so it doesn't regress, it'd be really nice :)
WebGL support on Windows <!-- Please describe your changes on the following line: --> This is the final step to provide WebGL support on Windows ;) Some Related PRs already merged in webrender and offscreen-gl-context: servo/surfman#64 servo/webrender#432 --- - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). - [X] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13840) <!-- Reviewable:end -->
…render_dispatcher); r=emilio <!-- Please describe your changes on the following line: --> This is the final step to provide WebGL support on Windows ;) Some Related PRs already merged in webrender and offscreen-gl-context: servo/surfman#64 servo/webrender#432 --- - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). - [X] There are tests for these changes OR - [ ] These changes do not require tests because _____ Source-Repo: https://github.com/servo/servo Source-Revision: 4216224f9cc7f3430db2b59f4d77061824ba7a1e
…render_dispatcher); r=emilio <!-- Please describe your changes on the following line: --> This is the final step to provide WebGL support on Windows ;) Some Related PRs already merged in webrender and offscreen-gl-context: servo/surfman#64 servo/webrender#432 --- - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). - [X] There are tests for these changes OR - [ ] These changes do not require tests because _____ Source-Repo: https://github.com/servo/servo Source-Revision: 4216224f9cc7f3430db2b59f4d77061824ba7a1e UltraBlame original commit: c57538159933d9eb1840437dfa9c9860220dd805
…render_dispatcher); r=emilio <!-- Please describe your changes on the following line: --> This is the final step to provide WebGL support on Windows ;) Some Related PRs already merged in webrender and offscreen-gl-context: servo/surfman#64 servo/webrender#432 --- - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). - [X] There are tests for these changes OR - [ ] These changes do not require tests because _____ Source-Repo: https://github.com/servo/servo Source-Revision: 4216224f9cc7f3430db2b59f4d77061824ba7a1e UltraBlame original commit: c57538159933d9eb1840437dfa9c9860220dd805
…render_dispatcher); r=emilio <!-- Please describe your changes on the following line: --> This is the final step to provide WebGL support on Windows ;) Some Related PRs already merged in webrender and offscreen-gl-context: servo/surfman#64 servo/webrender#432 --- - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). - [X] There are tests for these changes OR - [ ] These changes do not require tests because _____ Source-Repo: https://github.com/servo/servo Source-Revision: 4216224f9cc7f3430db2b59f4d77061824ba7a1e UltraBlame original commit: c57538159933d9eb1840437dfa9c9860220dd805
WGL implementation, now the servo webgl triangle demo runs on windows ;)