[wgl] Create a hidden window per instance (#4603)

This commit is contained in:
Zoxc 2023-10-30 06:10:01 +01:00 committed by GitHub
parent 37fe6a5424
commit ff1ba0b4fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 138 additions and 48 deletions

View File

@ -42,6 +42,13 @@ Bottom level categories:
For naga changelogs at or before v0.14.0. See [naga's changelog](naga/CHANGELOG.md). For naga changelogs at or before v0.14.0. See [naga's changelog](naga/CHANGELOG.md).
### Bug Fixes
#### WGL
- Create a hidden window per `wgpu::Instance` instead of sharing a global one.
## v0.18.0 (2023-10-25) ## v0.18.0 (2023-10-25)
### Desktop OpenGL 3.3+ Support on Windows ### Desktop OpenGL 3.3+ Support on Windows

View File

@ -13,7 +13,11 @@ use std::{
mem, mem,
os::raw::c_int, os::raw::c_int,
ptr, ptr,
sync::Arc, sync::{
mpsc::{sync_channel, SyncSender},
Arc,
},
thread,
time::Duration, time::Duration,
}; };
use wgt::InstanceFlags; use wgt::InstanceFlags;
@ -31,8 +35,8 @@ use winapi::{
PIXELFORMATDESCRIPTOR, PIXELFORMATDESCRIPTOR,
}, },
winuser::{ winuser::{
CreateWindowExA, DefWindowProcA, GetDC, RegisterClassExA, ReleaseDC, CS_OWNDC, CreateWindowExA, DefWindowProcA, DestroyWindow, GetDC, RegisterClassExA, ReleaseDC,
WNDCLASSEXA, CS_OWNDC, WNDCLASSEXA,
}, },
}, },
}; };
@ -69,7 +73,7 @@ impl AdapterContext {
.try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS)) .try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
.expect("Could not lock adapter context. This is most-likely a deadlock."); .expect("Could not lock adapter context. This is most-likely a deadlock.");
inner.context.make_current(inner.device).unwrap(); inner.context.make_current(inner.device.dc).unwrap();
AdapterContextLock { inner } AdapterContextLock { inner }
} }
@ -134,7 +138,7 @@ unsafe impl Sync for WglContext {}
struct Inner { struct Inner {
gl: glow::Context, gl: glow::Context,
device: HDC, device: InstanceDevice,
context: WglContext, context: WglContext,
} }
@ -219,7 +223,7 @@ unsafe fn setup_pixel_format(dc: HDC) -> Result<(), crate::InstanceError> {
Ok(()) Ok(())
} }
fn create_global_device_context() -> Result<HDC, crate::InstanceError> { fn create_global_window_class() -> Result<CString, crate::InstanceError> {
let instance = unsafe { GetModuleHandleA(ptr::null()) }; let instance = unsafe { GetModuleHandleA(ptr::null()) };
if instance.is_null() { if instance.is_null() {
return Err(crate::InstanceError::with_source( return Err(crate::InstanceError::with_source(
@ -269,12 +273,79 @@ fn create_global_device_context() -> Result<HDC, crate::InstanceError> {
)); ));
} }
// We intentionally leak the window class as we only need one per process.
Ok(name)
}
fn get_global_window_class() -> Result<CString, crate::InstanceError> {
static GLOBAL: Lazy<Result<CString, crate::InstanceError>> =
Lazy::new(create_global_window_class);
GLOBAL.clone()
}
struct InstanceDevice {
dc: HDC,
/// This is used to keep the thread owning `dc` alive until this struct is dropped.
_tx: SyncSender<()>,
}
fn create_instance_device() -> Result<InstanceDevice, crate::InstanceError> {
#[derive(Clone, Copy)]
struct SendDc(HDC);
unsafe impl Sync for SendDc {}
unsafe impl Send for SendDc {}
struct Window {
window: HWND,
}
impl Drop for Window {
fn drop(&mut self) {
unsafe {
if DestroyWindow(self.window) == FALSE {
log::error!("failed to destroy window {}", Error::last_os_error());
}
};
}
}
struct DeviceContextHandle {
dc: HDC,
window: HWND,
}
impl Drop for DeviceContextHandle {
fn drop(&mut self) {
unsafe {
ReleaseDC(self.window, self.dc);
};
}
}
let window_class = get_global_window_class()?;
let (drop_tx, drop_rx) = sync_channel(0);
let (setup_tx, setup_rx) = sync_channel(0);
// We spawn a thread which owns the hidden window for this instance.
thread::Builder::new()
.stack_size(256 * 1024)
.name("wgpu-hal WGL Instance Thread".to_owned())
.spawn(move || {
let setup = (|| {
let instance = unsafe { GetModuleHandleA(ptr::null()) };
if instance.is_null() {
return Err(crate::InstanceError::with_source(
String::from("unable to get executable instance"),
Error::last_os_error(),
));
}
// Create a hidden window since we don't pass `WS_VISIBLE`. // Create a hidden window since we don't pass `WS_VISIBLE`.
let window = unsafe { let window = unsafe {
CreateWindowExA( CreateWindowExA(
0, 0,
name.as_ptr(), window_class.as_ptr(),
name.as_ptr(), window_class.as_ptr(),
0, 0,
0, 0,
0, 0,
@ -292,31 +363,42 @@ fn create_global_device_context() -> Result<HDC, crate::InstanceError> {
Error::last_os_error(), Error::last_os_error(),
)); ));
} }
let dc = unsafe { GetDC(window) }; let window = Window { window };
let dc = unsafe { GetDC(window.window) };
if dc.is_null() { if dc.is_null() {
return Err(crate::InstanceError::with_source( return Err(crate::InstanceError::with_source(
String::from("unable to create memory device"), String::from("unable to create memory device"),
Error::last_os_error(), Error::last_os_error(),
)); ));
} }
unsafe { setup_pixel_format(dc)? }; let dc = DeviceContextHandle {
dc,
window: window.window,
};
unsafe { setup_pixel_format(dc.dc)? };
// We intentionally leak the window class, window and device context handle to avoid Ok((window, dc))
// spawning a thread to destroy them. We cannot use `DestroyWindow` and `ReleaseDC` on })();
// different threads.
Ok(dc) match setup {
} Ok((_window, dc)) => {
setup_tx.send(Ok(SendDc(dc.dc))).unwrap();
// Wait for the shutdown event to free the window and device context handle.
drop_rx.recv().ok();
}
Err(err) => {
setup_tx.send(Err(err)).unwrap();
}
}
})
.map_err(|e| {
crate::InstanceError::with_source(String::from("unable to create instance thread"), e)
})?;
fn get_global_device_context() -> Result<HDC, crate::InstanceError> { let dc = setup_rx.recv().unwrap()?.0;
#[derive(Clone, Copy)]
struct SendDc(HDC);
unsafe impl Sync for SendDc {}
unsafe impl Send for SendDc {}
static GLOBAL: Lazy<Result<SendDc, crate::InstanceError>> = Ok(InstanceDevice { dc, _tx: drop_tx })
Lazy::new(|| create_global_device_context().map(SendDc));
GLOBAL.clone().map(|dc| dc.0)
} }
impl crate::Instance<super::Api> for Instance { impl crate::Instance<super::Api> for Instance {
@ -330,7 +412,8 @@ impl crate::Instance<super::Api> for Instance {
)); ));
} }
let dc = get_global_device_context()?; let device = create_instance_device()?;
let dc = device.dc;
let context = unsafe { wglCreateContext(dc) }; let context = unsafe { wglCreateContext(dc) };
if context.is_null() { if context.is_null() {
@ -420,7 +503,7 @@ impl crate::Instance<super::Api> for Instance {
Ok(Instance { Ok(Instance {
inner: Arc::new(Mutex::new(Inner { inner: Arc::new(Mutex::new(Inner {
device: dc, device,
gl, gl,
context, context,
})), })),