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).
|
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
|
||||||
|
|||||||
@ -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,54 +273,132 @@ fn create_global_device_context() -> Result<HDC, crate::InstanceError> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a hidden window since we don't pass `WS_VISIBLE`.
|
// We intentionally leak the window class as we only need one per process.
|
||||||
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, window and device context handle to avoid
|
Ok(name)
|
||||||
// spawning a thread to destroy them. We cannot use `DestroyWindow` and `ReleaseDC` on
|
|
||||||
// different threads.
|
|
||||||
|
|
||||||
Ok(dc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)]
|
#[derive(Clone, Copy)]
|
||||||
struct SendDc(HDC);
|
struct SendDc(HDC);
|
||||||
unsafe impl Sync for SendDc {}
|
unsafe impl Sync for SendDc {}
|
||||||
unsafe impl Send for SendDc {}
|
unsafe impl Send for SendDc {}
|
||||||
|
|
||||||
static GLOBAL: Lazy<Result<SendDc, crate::InstanceError>> =
|
struct Window {
|
||||||
Lazy::new(|| create_global_device_context().map(SendDc));
|
window: HWND,
|
||||||
GLOBAL.clone().map(|dc| dc.0)
|
}
|
||||||
|
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 {
|
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,
|
||||||
})),
|
})),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user