[wgpu-hal] Allow importing external WGL contexts as with EGL (#6152)

This commit is contained in:
Marijn Suijten 2024-09-04 17:11:47 +02:00 committed by GitHub
parent 4e78829d82
commit 960555a426
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 96 additions and 30 deletions

View File

@ -118,6 +118,7 @@ By @teoxoy [#6134](https://github.com/gfx-rs/wgpu/pull/6134).
- Replace `winapi` code in WGL wrapper to use the `windows` crate. By @MarijnS95 in [#6006](https://github.com/gfx-rs/wgpu/pull/6006)
- Update `glutin` to `0.31` with `glutin-winit` crate. By @MarijnS95 in [#6150](https://github.com/gfx-rs/wgpu/pull/6150) and [#6176](https://github.com/gfx-rs/wgpu/pull/6176)
- Implement `Adapter::new_external()` for WGL (just like EGL) to import an external OpenGL ES context. By @MarijnS95 in [#6152](https://github.com/gfx-rs/wgpu/pull/6152)
#### DX12

13
Cargo.lock generated
View File

@ -1330,12 +1330,14 @@ dependencies = [
"core-foundation",
"dispatch",
"glutin_egl_sys",
"glutin_wgl_sys 0.5.0",
"icrate",
"libloading",
"objc2",
"once_cell",
"raw-window-handle 0.5.2",
"wayland-sys",
"windows-sys 0.48.0",
"x11-dl",
]
@ -1361,6 +1363,15 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "glutin_wgl_sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead"
dependencies = [
"gl_generator",
]
[[package]]
name = "glutin_wgl_sys"
version = "0.6.0"
@ -3669,7 +3680,7 @@ dependencies = [
"glow",
"glutin",
"glutin-winit",
"glutin_wgl_sys",
"glutin_wgl_sys 0.6.0",
"gpu-alloc",
"gpu-allocator",
"gpu-descriptor",

View File

@ -206,8 +206,8 @@ env_logger.workspace = true
glam.workspace = true # for ray-traced-triangle example
winit.workspace = true # for "halmark" example
[target.'cfg(not(any(target_arch = "wasm32", windows, target_os = "ios")))'.dev-dependencies]
glutin-winit = { workspace = true, features = ["egl", "wayland", "x11"] } # for "raw-gles" example
glutin = { workspace = true, features = ["egl", "wayland", "x11"] } # for "raw-gles" example
[target.'cfg(not(any(target_arch = "wasm32", target_os = "ios")))'.dev-dependencies]
glutin-winit = { workspace = true, features = ["egl", "wgl", "wayland", "x11"] } # for "raw-gles" example
glutin = { workspace = true, features = ["egl", "wgl", "wayland", "x11"] } # for "raw-gles" example
rwh_05 = { version = "0.5", package = "raw-window-handle" } # temporary compatibility for glutin-winit in "raw-gles" example
winit = { workspace = true, features = ["rwh_05"] } # for "raw-gles" example

View File

@ -10,7 +10,7 @@
extern crate wgpu_hal as hal;
#[cfg(not(any(windows, target_arch = "wasm32", target_os = "ios")))]
#[cfg(not(any(target_arch = "wasm32", target_os = "ios")))]
fn main() {
use std::{ffi::CString, num::NonZeroU32};
@ -255,7 +255,6 @@ fn main() {
}
#[cfg(any(
windows,
all(target_arch = "wasm32", not(target_os = "emscripten")),
target_os = "ios"
))]
@ -264,7 +263,6 @@ fn main() {
}
#[cfg(not(any(
windows,
all(target_arch = "wasm32", not(target_os = "emscripten")),
target_os = "ios"
)))]

View File

@ -379,7 +379,7 @@ struct EglContextLock<'a> {
display: khronos_egl::Display,
}
/// A guard containing a lock to an [`AdapterContext`]
/// A guard containing a lock to an [`AdapterContext`], while the GL context is kept current.
pub struct AdapterContextLock<'a> {
glow: MutexGuard<'a, ManuallyDrop<glow::Context>>,
egl: Option<EglContextLock<'a>>,
@ -1082,7 +1082,9 @@ impl crate::Instance for Instance {
unsafe { gl.debug_message_callback(super::gl_debug_message_callback) };
}
// Avoid accidental drop when the context is not current.
// Wrap in ManuallyDrop to make it easier to "current" the GL context before dropping this
// GLOW context, which could also happen if a panic occurs after we uncurrent the context
// below but before AdapterContext is constructed.
let gl = ManuallyDrop::new(gl);
inner.egl.unmake_current();

View File

@ -1,11 +1,3 @@
use glow::HasContext;
use glutin_wgl_sys::wgl_extra::{
Wgl, CONTEXT_CORE_PROFILE_BIT_ARB, CONTEXT_DEBUG_BIT_ARB, CONTEXT_FLAGS_ARB,
CONTEXT_PROFILE_MASK_ARB,
};
use once_cell::sync::Lazy;
use parking_lot::{Mutex, MutexGuard, RwLock};
use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
use std::{
collections::HashSet,
ffi::{c_void, CStr, CString},
@ -19,6 +11,15 @@ use std::{
thread,
time::Duration,
};
use glow::HasContext;
use glutin_wgl_sys::wgl_extra::{
Wgl, CONTEXT_CORE_PROFILE_BIT_ARB, CONTEXT_DEBUG_BIT_ARB, CONTEXT_FLAGS_ARB,
CONTEXT_PROFILE_MASK_ARB,
};
use once_cell::sync::Lazy;
use parking_lot::{Mutex, MutexGuard, RwLock};
use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
use wgt::InstanceFlags;
use windows::{
core::{Error, PCSTR},
@ -48,7 +49,10 @@ impl AdapterContext {
}
pub fn raw_context(&self) -> *mut c_void {
self.inner.lock().context.context.0
match self.inner.lock().context {
Some(ref wgl) => wgl.context.0,
None => ptr::null_mut(),
}
}
/// Obtain a lock to the WGL context and get handle to the [`glow::Context`] that can be used to
@ -62,7 +66,9 @@ 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.dc).unwrap();
if let Some(wgl) = &inner.context {
wgl.make_current(inner.device.dc).unwrap()
};
AdapterContextLock { inner }
}
@ -79,14 +85,15 @@ 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(device)
.map(|()| AdapterContextLock { inner })
if let Some(wgl) = &inner.context {
wgl.make_current(device)?;
}
Ok(AdapterContextLock { inner })
}
}
/// A guard containing a lock to an [`AdapterContext`]
/// A guard containing a lock to an [`AdapterContext`], while the GL context is kept current.
pub struct AdapterContextLock<'a> {
inner: MutexGuard<'a, Inner>,
}
@ -101,7 +108,9 @@ impl<'a> std::ops::Deref for AdapterContextLock<'a> {
impl<'a> Drop for AdapterContextLock<'a> {
fn drop(&mut self) {
self.inner.context.unmake_current().unwrap();
if let Some(wgl) = &self.inner.context {
wgl.unmake_current().unwrap()
}
}
}
@ -136,7 +145,7 @@ unsafe impl Sync for WglContext {}
struct Inner {
gl: ManuallyDrop<glow::Context>,
device: InstanceDevice,
context: WglContext,
context: Option<WglContext>,
}
impl Drop for Inner {
@ -150,8 +159,14 @@ impl Drop for Inner {
// Context must be current when dropped. See safety docs on
// `glow::HasContext`.
self.context.make_current(self.device.dc).unwrap();
let _guard = CurrentGuard(&self.context);
//
// NOTE: This is only set to `None` by `Adapter::new_external` which
// requires the context to be current when anything that may be holding
// the `Arc<AdapterShared>` is dropped.
let _guard = self.context.as_ref().map(|wgl| {
wgl.make_current(self.device.dc).unwrap();
CurrentGuard(wgl)
});
// SAFETY: Field not used after this.
unsafe { ManuallyDrop::drop(&mut self.gl) };
}
@ -510,7 +525,9 @@ impl crate::Instance for Instance {
unsafe { gl.debug_message_callback(super::gl_debug_message_callback) };
}
// Avoid accidental drop when the context is not current.
// Wrap in ManuallyDrop to make it easier to "current" the GL context before dropping this
// GLOW context, which could also happen if a panic occurs after we uncurrent the context
// below but before Inner is constructed.
let gl = ManuallyDrop::new(gl);
context.unmake_current().map_err(|e| {
crate::InstanceError::with_source(
@ -523,7 +540,7 @@ impl crate::Instance for Instance {
inner: Arc::new(Mutex::new(Inner {
device,
gl,
context,
context: Some(context),
})),
srgb_capable,
})
@ -565,6 +582,43 @@ impl crate::Instance for Instance {
}
}
impl super::Adapter {
/// Creates a new external adapter using the specified loader function.
///
/// # Safety
///
/// - The underlying OpenGL ES context must be current.
/// - The underlying OpenGL ES context must be current when interfacing with any objects returned by
/// wgpu-hal from this adapter.
/// - The underlying OpenGL ES context must be current when dropping this adapter and when
/// dropping any objects returned from this adapter.
pub unsafe fn new_external(
fun: impl FnMut(&str) -> *const c_void,
) -> Option<crate::ExposedAdapter<super::Api>> {
let context = unsafe { glow::Context::from_loader_function(fun) };
unsafe {
Self::expose(AdapterContext {
inner: Arc::new(Mutex::new(Inner {
gl: ManuallyDrop::new(context),
device: create_instance_device().ok()?,
context: None,
})),
})
}
}
pub fn adapter_context(&self) -> &AdapterContext {
&self.shared.context
}
}
impl super::Device {
/// Returns the underlying WGL context.
pub fn context(&self) -> &AdapterContext {
&self.shared.context
}
}
struct DeviceContextHandle {
device: Gdi::HDC,
window: Foundation::HWND,