mirror of
https://github.com/gfx-rs/wgpu.git
synced 2025-12-08 21:26:17 +00:00
[wgl] Create a hidden window per instance (#4603)
This commit is contained in:
parent
37fe6a5424
commit
ff1ba0b4fe
@ -42,6 +42,13 @@ Bottom level categories:
|
||||
|
||||
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)
|
||||
|
||||
### Desktop OpenGL 3.3+ Support on Windows
|
||||
|
||||
@ -13,7 +13,11 @@ use std::{
|
||||
mem,
|
||||
os::raw::c_int,
|
||||
ptr,
|
||||
sync::Arc,
|
||||
sync::{
|
||||
mpsc::{sync_channel, SyncSender},
|
||||
Arc,
|
||||
},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
use wgt::InstanceFlags;
|
||||
@ -31,8 +35,8 @@ use winapi::{
|
||||
PIXELFORMATDESCRIPTOR,
|
||||
},
|
||||
winuser::{
|
||||
CreateWindowExA, DefWindowProcA, GetDC, RegisterClassExA, ReleaseDC, CS_OWNDC,
|
||||
WNDCLASSEXA,
|
||||
CreateWindowExA, DefWindowProcA, DestroyWindow, GetDC, RegisterClassExA, ReleaseDC,
|
||||
CS_OWNDC, WNDCLASSEXA,
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -69,7 +73,7 @@ impl AdapterContext {
|
||||
.try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
|
||||
.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 }
|
||||
}
|
||||
@ -134,7 +138,7 @@ unsafe impl Sync for WglContext {}
|
||||
|
||||
struct Inner {
|
||||
gl: glow::Context,
|
||||
device: HDC,
|
||||
device: InstanceDevice,
|
||||
context: WglContext,
|
||||
}
|
||||
|
||||
@ -219,7 +223,7 @@ unsafe fn setup_pixel_format(dc: HDC) -> Result<(), crate::InstanceError> {
|
||||
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()) };
|
||||
if instance.is_null() {
|
||||
return Err(crate::InstanceError::with_source(
|
||||
@ -269,54 +273,132 @@ fn create_global_device_context() -> Result<HDC, crate::InstanceError> {
|
||||
));
|
||||
}
|
||||
|
||||
// Create a hidden window since we don't pass `WS_VISIBLE`.
|
||||
let window = unsafe {
|
||||
CreateWindowExA(
|
||||
0,
|
||||
name.as_ptr(),
|
||||
name.as_ptr(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
instance,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
if window.is_null() {
|
||||
return Err(crate::InstanceError::with_source(
|
||||
String::from("unable to create hidden instance window"),
|
||||
Error::last_os_error(),
|
||||
));
|
||||
}
|
||||
let dc = unsafe { GetDC(window) };
|
||||
if dc.is_null() {
|
||||
return Err(crate::InstanceError::with_source(
|
||||
String::from("unable to create memory device"),
|
||||
Error::last_os_error(),
|
||||
));
|
||||
}
|
||||
unsafe { setup_pixel_format(dc)? };
|
||||
// We intentionally leak the window class as we only need one per process.
|
||||
|
||||
// We intentionally leak the window class, window and device context handle to avoid
|
||||
// spawning a thread to destroy them. We cannot use `DestroyWindow` and `ReleaseDC` on
|
||||
// different threads.
|
||||
|
||||
Ok(dc)
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
fn get_global_device_context() -> Result<HDC, crate::InstanceError> {
|
||||
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 {}
|
||||
|
||||
static GLOBAL: Lazy<Result<SendDc, crate::InstanceError>> =
|
||||
Lazy::new(|| create_global_device_context().map(SendDc));
|
||||
GLOBAL.clone().map(|dc| dc.0)
|
||||
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`.
|
||||
let window = unsafe {
|
||||
CreateWindowExA(
|
||||
0,
|
||||
window_class.as_ptr(),
|
||||
window_class.as_ptr(),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
instance,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
if window.is_null() {
|
||||
return Err(crate::InstanceError::with_source(
|
||||
String::from("unable to create hidden instance window"),
|
||||
Error::last_os_error(),
|
||||
));
|
||||
}
|
||||
let window = Window { window };
|
||||
|
||||
let dc = unsafe { GetDC(window.window) };
|
||||
if dc.is_null() {
|
||||
return Err(crate::InstanceError::with_source(
|
||||
String::from("unable to create memory device"),
|
||||
Error::last_os_error(),
|
||||
));
|
||||
}
|
||||
let dc = DeviceContextHandle {
|
||||
dc,
|
||||
window: window.window,
|
||||
};
|
||||
unsafe { setup_pixel_format(dc.dc)? };
|
||||
|
||||
Ok((window, 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)
|
||||
})?;
|
||||
|
||||
let dc = setup_rx.recv().unwrap()?.0;
|
||||
|
||||
Ok(InstanceDevice { dc, _tx: drop_tx })
|
||||
}
|
||||
|
||||
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) };
|
||||
if context.is_null() {
|
||||
@ -420,7 +503,7 @@ impl crate::Instance<super::Api> for Instance {
|
||||
|
||||
Ok(Instance {
|
||||
inner: Arc::new(Mutex::new(Inner {
|
||||
device: dc,
|
||||
device,
|
||||
gl,
|
||||
context,
|
||||
})),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user