Skip to content

Commit

Permalink
[wgpu-hal] Allow importing external WGL contexts as with EGL
Browse files Browse the repository at this point in the history
  • Loading branch information
MarijnS95 authored and ErichDonGubler committed Sep 4, 2024
1 parent ee35b0e commit 2c1ea94
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 30 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
13 changes: 12 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions wgpu-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 1 addition & 3 deletions wgpu-hal/examples/raw-gles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -255,7 +255,6 @@ fn main() {
}

#[cfg(any(
windows,
all(target_arch = "wasm32", not(target_os = "emscripten")),
target_os = "ios"
))]
Expand All @@ -264,7 +263,6 @@ fn main() {
}

#[cfg(not(any(
windows,
all(target_arch = "wasm32", not(target_os = "emscripten")),
target_os = "ios"
)))]
Expand Down
6 changes: 4 additions & 2 deletions wgpu-hal/src/gles/egl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>>,
Expand Down Expand Up @@ -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();

Expand Down
96 changes: 75 additions & 21 deletions wgpu-hal/src/gles/wgl.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand All @@ -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},
Expand Down Expand Up @@ -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
Expand All @@ -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 }
}
Expand All @@ -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>,
}
Expand All @@ -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()
}
}
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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) };
}
Expand Down Expand Up @@ -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(
Expand All @@ -523,7 +540,7 @@ impl crate::Instance for Instance {
inner: Arc::new(Mutex::new(Inner {
device,
gl,
context,
context: Some(context),
})),
srgb_capable,
})
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 2c1ea94

Please sign in to comment.