/// A simple wrapper around an X11 or Wayland display handle. /// Since the logic in this file doesn't actually need to directly /// persist a wayland connection handle, the only load-bearing /// enum variant is the X11 variant #[derive(Debug)] enum DisplayRef {
X11(ptr::NonNull<raw::c_void>),
Wayland,
}
impl DisplayRef { /// Convenience for getting the underlying pointer fn as_ptr(&self) -> *mut raw::c_void { match *self { Self::X11(ptr) => ptr.as_ptr(), Self::Wayland => unreachable!(),
}
}
}
/// DisplayOwner ties the lifetime of the system display handle /// to that of the loaded library. /// It implements Drop to ensure that the display handle is closed /// prior to unloading the library so that we don't leak the /// associated file descriptors #[derive(Debug)] struct DisplayOwner {
library: libloading::Library,
display: DisplayRef,
}
impl Drop for DisplayOwner { fn drop(&mutself) { matchself.display {
DisplayRef::X11(ptr) => unsafe { let func: libloading::Symbol<XCloseDisplayFun> = self.library.get(b"XCloseDisplay\0").unwrap();
func(ptr.as_ptr());
},
DisplayRef::Wayland => {}
}
}
}
fn open_x_display() -> Option<DisplayOwner> {
log::debug!("Loading X11 library to get the current display"); unsafe { let library = find_library(&["libX11.so.6", "libX11.so"])?; let func: libloading::Symbol<XOpenDisplayFun> = library.get(b"XOpenDisplay\0").unwrap(); let result = func(ptr::null());
ptr::NonNull::new(result).map(|ptr| DisplayOwner {
display: DisplayRef::X11(ptr),
library,
})
}
}
fn test_wayland_display() -> Option<DisplayOwner> { /* We try to connect and disconnect here to simply ensure there *isanactivewaylanddisplayavailable.
*/
log::debug!("Loading Wayland library to get the current display"); let library = unsafe { let client_library = find_library(&["libwayland-client.so.0", "libwayland-client.so"])?; let wl_display_connect: libloading::Symbol<WlDisplayConnectFun> =
client_library.get(b"wl_display_connect\0").unwrap(); let wl_display_disconnect: libloading::Symbol<WlDisplayDisconnectFun> =
client_library.get(b"wl_display_disconnect\0").unwrap(); let display = ptr::NonNull::new(wl_display_connect(ptr::null()))?;
wl_display_disconnect(display.as_ptr());
find_library(&["libwayland-egl.so.1", "libwayland-egl.so"])?
};
Some(DisplayOwner {
library,
display: DisplayRef::Wayland,
})
}
#[derive(Clone, Copy, Debug)] enum SrgbFrameBufferKind { /// No support for SRGB surface
None, /// Using EGL 1.5's support for colorspaces
Core, /// Using EGL_KHR_gl_colorspace
Khr,
}
letmut attributes = Vec::with_capacity(9); for tier_max in (0..tiers.len()).rev() { let name = tiers[tier_max].0;
log::debug!("\tTrying {}", name);
attributes.clear(); for &(_, tier_attr) in tiers[..=tier_max].iter() {
attributes.extend_from_slice(tier_attr);
} // make sure the Alpha is enough to support sRGB match srgb_kind {
SrgbFrameBufferKind::None => {}
_ => {
attributes.push(khronos_egl::ALPHA_SIZE);
attributes.push(8);
}
}
attributes.push(khronos_egl::NONE);
match egl.choose_first_config(display, &attributes) {
Ok(Some(config)) => { if tier_max == 1 { //Note: this has been confirmed to malfunction on Intel+NV laptops, // but also on Angle.
log::warn!("EGL says it can present to the window but not natively",);
} // Android emulator can't natively present either. let tier_threshold = if cfg!(target_os = "android") || cfg!(windows) { 1
} else { 2
}; return Ok((config, tier_max >= tier_threshold));
}
Ok(None) => {
log::warn!("No config found!");
}
Err(e) => {
log::error!("error in choose_first_config: {:?}", e);
}
}
}
// TODO: include diagnostic details that are currently logged
Err(crate::InstanceError::new(String::from( "unable to find an acceptable EGL framebuffer configuration",
)))
}
/// A wrapper around a [`glow::Context`] and the required EGL context that uses locking to guarantee /// exclusive access when shared with multiple threads. pubstruct AdapterContext {
glow: Mutex<ManuallyDrop<glow::Context>>,
egl: Option<EglContext>,
}
unsafeimpl Sync for AdapterContext {} unsafeimpl Send for AdapterContext {}
/// Returns the EGL instance. /// /// This provides access to EGL functions and the ability to load GL and EGL extension functions. pubfn egl_instance(&self) -> Option<&EglInstance> { self.egl.as_ref().map(|egl| &*egl.instance)
}
/// Returns the EGLDisplay corresponding to the adapter context. /// /// Returns [`None`] if the adapter was externally created. pubfn raw_display(&self) -> Option<&khronos_egl::Display> { self.egl.as_ref().map(|egl| &egl.display)
}
/// Returns the EGL version the adapter context was created with. /// /// Returns [`None`] if the adapter was externally created. pubfn egl_version(&self) -> Option<(i32, i32)> { self.egl.as_ref().map(|egl| egl.version)
}
impl Drop for AdapterContext { fn drop(&mutself) { struct CurrentGuard<'a>(&'a EglContext); impl Drop for CurrentGuard<'_> { fn drop(&mutself) { self.0.unmake_current();
}
}
// Context must be current when dropped. See safety docs on // `glow::HasContext`. // // 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.egl.as_ref().map(|egl| {
egl.make_current();
CurrentGuard(egl)
}); let glow = self.glow.get_mut(); // SAFETY: Field not used after this. unsafe { ManuallyDrop::drop(glow) };
}
}
/// A guard containing a lock to an [`AdapterContext`], while the GL context is kept current. pubstruct AdapterContextLock<'a> {
glow: MutexGuard<'a, ManuallyDrop<glow::Context>>,
egl: Option<EglContextLock<'a>>,
}
impl<'a> std::ops::Deref for AdapterContextLock<'a> { type Target = glow::Context;
impl AdapterContext { /// Get's the [`glow::Context`] without waiting for a lock /// /// # Safety /// /// This should only be called when you have manually made sure that the current thread has made /// the EGL context current and that no other thread also has the EGL context current. /// Additionally, you must manually make the EGL context **not** current after you are done with /// it, so that future calls to `lock()` will not fail. /// /// > **Note:** Calling this function **will** still lock the [`glow::Context`] which adds an /// > extra safe-guard against accidental concurrent access to the context. pubunsafefn get_without_egl_lock(&self) -> MappedMutexGuard<glow::Context> { let guard = self
.glow
.try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
.expect("Could not lock adapter context. This is most-likely a deadlock.");
MutexGuard::map(guard, |glow| &mut **glow)
}
/// Obtain a lock to the EGL context and get handle to the [`glow::Context`] that can be used to /// do rendering. #[track_caller] pubfn lock<'a>(&'a self) -> AdapterContextLock<'a> { let glow = self
.glow // Don't lock forever. If it takes longer than 1 second to get the lock we've got a // deadlock and should panic to show where we got stuck
.try_lock_for(Duration::from_secs(CONTEXT_LOCK_TIMEOUT_SECS))
.expect("Could not lock adapter context. This is most-likely a deadlock.");
#[derive(Debug)] struct Inner { /// Note: the context contains a dummy pbuffer (1x1). /// Required for `eglMakeCurrent` on platforms that doesn't supports `EGL_KHR_surfaceless_context`.
egl: EglContext, #[allow(unused)]
version: (i32, i32),
supports_native_window: bool,
config: khronos_egl::Config, #[cfg_attr(Emscripten, allow(dead_code))]
wl_display: Option<*mut raw::c_void>, #[cfg_attr(Emscripten, allow(dead_code))]
force_gles_minor_version: wgt::Gles3MinorVersion, /// Method by which the framebuffer should support srgb
srgb_kind: SrgbFrameBufferKind,
}
// Different calls to `eglGetPlatformDisplay` may return the same `Display`, making it a global // state of all our `EglContext`s. This forces us to track the number of such context to prevent // terminating the display if it's currently used by another `EglContext`. static DISPLAYS_REFERENCE_COUNT: Lazy<Mutex<HashMap<usize, usize>>> = Lazy::new(Default::default);
// We don't need to check the reference count here since according to the `eglInitialize` // documentation, initializing an already initialized EGL display connection has no effect // besides returning the version numbers.
egl.initialize(display)
}
fn terminate_display(
egl: &EglInstance,
display: khronos_egl::Display,
) -> Result<(), khronos_egl::Error> { let key = &(display.as_ptr() as usize); letmut guard = DISPLAYS_REFERENCE_COUNT.lock(); let count_ref = guard
.get_mut(key)
.expect("Attempted to decref a display before incref was called");
if *count_ref > 1 {
*count_ref -= 1;
Ok(())
} else {
guard.remove(key);
egl.terminate(display)
}
}
impl Inner { fn create(
flags: wgt::InstanceFlags,
egl: Arc<EglInstance>,
display: khronos_egl::Display,
force_gles_minor_version: wgt::Gles3MinorVersion,
) -> Result<Self, crate::InstanceError> { let version = initialize_display(&egl, display).map_err(|e| { crate::InstanceError::with_source(
String::from("failed to initialize EGL display connection"),
e,
)
})?; let vendor = egl
.query_string(Some(display), khronos_egl::VENDOR)
.unwrap(); let display_extensions = egl
.query_string(Some(display), khronos_egl::EXTENSIONS)
.unwrap()
.to_string_lossy();
log::debug!("Display vendor {:?}, version {:?}", vendor, version,);
log::debug!( "Display extensions: {:#?}",
display_extensions.split_whitespace().collect::<Vec<_>>()
);
let srgb_kind = if version >= (1, 5) {
log::debug!("\tEGL surface: +srgb");
SrgbFrameBufferKind::Core
} elseif display_extensions.contains("EGL_KHR_gl_colorspace") {
log::debug!("\tEGL surface: +srgb khr");
SrgbFrameBufferKind::Khr
} else {
log::warn!("\tEGL surface: -srgb");
SrgbFrameBufferKind::None
};
impl Instance { pubfn raw_display(&self) -> khronos_egl::Display { self.inner
.try_lock()
.expect("Could not lock instance. This is most-likely a deadlock.")
.egl
.display
}
/// Returns the version of the EGL display. pubfn egl_version(&self) -> (i32, i32) { self.inner
.try_lock()
.expect("Could not lock instance. This is most-likely a deadlock.")
.version
}
pubfn egl_config(&self) -> khronos_egl::Config { self.inner
.try_lock()
.expect("Could not lock instance. This is most-likely a deadlock.")
.config
}
}
unsafeimpl Send for Instance {} unsafeimpl Sync for Instance {}
implcrate::Instance for Instance { type A = super::Api;
let ret = unsafe {
ndk_sys::ANativeWindow_setBuffersGeometry(
handle
.a_native_window
.as_ptr()
.cast::<ndk_sys::ANativeWindow>(), 0, 0,
format,
)
};
if ret != 0 { return Err(crate::InstanceError::new(format!( "error {ret} returned from ANativeWindow_setBuffersGeometry",
)));
}
} #[cfg(not(Emscripten))]
(Rwh::Wayland(_), raw_window_handle::RawDisplayHandle::Wayland(display_handle)) => { if inner
.wl_display
.map(|ptr| ptr != display_handle.display.as_ptr())
.unwrap_or(true)
{ /* Wayland displays are not sharable between surfaces so if the *surfacewereceivefromthishandleisfromadifferent *display,wemustre-initializethecontext. * *Seegfx-rs/gfx#3545
*/
log::warn!("Re-initializing Gles context due to Wayland window");
use std::ops::DerefMut; let display_attributes = [khronos_egl::ATTRIB_NONE];
letmut gl = unsafe {
glow::Context::from_loader_function(|name| {
inner
.egl
.instance
.get_proc_address(name)
.map_or(ptr::null(), |p| p as *const _)
})
};
// In contrast to OpenGL ES, OpenGL requires explicitly enabling sRGB conversions, // as otherwise the user has to do the sRGB conversion. if !matches!(inner.srgb_kind, SrgbFrameBufferKind::None) { unsafe { gl.enable(glow::FRAMEBUFFER_SRGB) };
}
// 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();
implsuper::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. pubunsafefn new_external(
fun: impl FnMut(&str) -> *const ffi::c_void,
) -> Option<crate::ExposedAdapter<super::Api>> { let context = unsafe { glow::Context::from_loader_function(fun) }; unsafe { Self::expose(AdapterContext {
glow: Mutex::new(ManuallyDrop::new(context)),
egl: None,
})
}
}
if !matches!(self.srgb_kind, SrgbFrameBufferKind::None) { // Disable sRGB conversions for `glBlitFramebuffer` as behavior does diverge between // drivers and formats otherwise and we want to ensure no sRGB conversions happen. unsafe { gl.disable(glow::FRAMEBUFFER_SRGB) };
}
// Note the Y-flipping here. GL's presentation is not flipped, // but main rendering is. Therefore, we Y-flip the output positions // in the shader, and also this blit. unsafe {
gl.blit_framebuffer( 0,
sc.extent.height as i32,
sc.extent.width as i32, 0, 0, 0,
sc.extent.width as i32,
sc.extent.height as i32,
glow::COLOR_BUFFER_BIT,
glow::NEAREST,
)
};
if !matches!(self.srgb_kind, SrgbFrameBufferKind::None) { unsafe { gl.enable(glow::FRAMEBUFFER_SRGB) };
}
implcrate::Surface for Surface { type A = super::Api;
unsafefn configure(
&self,
device: &super::Device,
config: &crate::SurfaceConfiguration,
) -> Result<(), crate::SurfaceError> { use raw_window_handle::RawWindowHandle as Rwh;
let (surface, wl_window) = matchunsafe { self.unconfigure_impl(device) } {
Some(pair) => pair,
None => { letmut wl_window = None; let (mut temp_xlib_handle, mut temp_xcb_handle); let native_window_ptr = match (self.wsi.kind, self.raw_window_handle) {
(WindowKind::Unknown | WindowKind::X11, Rwh::Xlib(handle)) => {
temp_xlib_handle = handle.window;
ptr::from_mut(&mut temp_xlib_handle).cast::<ffi::c_void>()
}
(WindowKind::AngleX11, Rwh::Xlib(handle)) => handle.window as *mut ffi::c_void,
(WindowKind::Unknown | WindowKind::X11, Rwh::Xcb(handle)) => {
temp_xcb_handle = handle.window;
ptr::from_mut(&mut temp_xcb_handle).cast::<ffi::c_void>()
}
(WindowKind::AngleX11, Rwh::Xcb(handle)) => {
handle.window.get() as *mut ffi::c_void
}
(WindowKind::Unknown, Rwh::AndroidNdk(handle)) => {
handle.a_native_window.as_ptr()
}
(WindowKind::Wayland, Rwh::Wayland(handle)) => { let library = &self.wsi.display_owner.as_ref().unwrap().library; let wl_egl_window_create: libloading::Symbol<WlEglWindowCreateFun> = unsafe { library.get(b"wl_egl_window_create\0") }.unwrap(); let window = unsafe { wl_egl_window_create(handle.surface.as_ptr(), 640, 480) }
.cast();
wl_window = Some(window);
window
} #[cfg(Emscripten)]
(WindowKind::Unknown, Rwh::Web(handle)) => handle.id as *mut ffi::c_void,
(WindowKind::Unknown, Rwh::Win32(handle)) => {
handle.hwnd.get() as *mut ffi::c_void
}
(WindowKind::Unknown, Rwh::AppKit(handle)) => { #[cfg(not(target_os = "macos"))] let window_ptr = handle.ns_view.as_ptr(); #[cfg(target_os = "macos")] let window_ptr = { use objc::{msg_send, runtime::Object, sel, sel_impl}; // ns_view always have a layer and don't need to verify that it exists. let layer: *mut Object =
msg_send![handle.ns_view.as_ptr().cast::<Object>(), layer];
layer.cast::<ffi::c_void>()
};
window_ptr
}
_ => {
log::warn!( "Initialized platform {:?} doesn't work with window {:?}", self.wsi.kind, self.raw_window_handle
); return Err(crate::SurfaceError::Other("incompatible window kind"));
}
};
letmut attributes = vec![
khronos_egl::RENDER_BUFFER, // We don't want any of the buffering done by the driver, because we // manage a swapchain on our side. // Some drivers just fail on surface creation seeing `EGL_SINGLE_BUFFER`. if cfg!(any(target_os = "android", target_os = "macos"))
|| cfg!(windows)
|| self.wsi.kind == WindowKind::AngleX11
{
khronos_egl::BACK_BUFFER
} else {
khronos_egl::SINGLE_BUFFER
},
]; if config.format.is_srgb() { matchself.srgb_kind {
SrgbFrameBufferKind::None => {}
SrgbFrameBufferKind::Core => {
attributes.push(khronos_egl::GL_COLORSPACE);
attributes.push(khronos_egl::GL_COLORSPACE_SRGB);
}
SrgbFrameBufferKind::Khr => {
attributes.push(EGL_GL_COLORSPACE_KHR as i32);
attributes.push(EGL_GL_COLORSPACE_SRGB_KHR as i32);
}
}
}
attributes.push(khronos_egl::ATTRIB_NONE as i32);
#[cfg(not(Emscripten))] let egl1_5 = self.egl.instance.upcast::<khronos_egl::EGL1_5>();
#[cfg(Emscripten)] let egl1_5: Option<&Arc<EglInstance>> = Some(&self.egl.instance);
// Careful, we can still be in 1.4 version even if `upcast` succeeds let raw_result = match egl1_5 {
Some(egl) ifself.wsi.kind != WindowKind::Unknown => { let attributes_usize = attributes
.into_iter()
.map(|v| v as usize)
.collect::<Vec<_>>(); unsafe {
egl.create_platform_window_surface( self.egl.display, self.config,
native_window_ptr,
&attributes_usize,
)
}
}
_ => unsafe { self.egl.instance.create_window_surface( self.egl.display, self.config,
native_window_ptr,
Some(&attributes),
)
},
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.