From 6d16d941ef424ce8d3ba0e3e5ffd84fc891379d0 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Wed, 6 Mar 2024 01:15:43 +0100 Subject: [PATCH] api: add extra EGL and GLX extensions This patch adds multiple new EGL extensions which are useful for implementing Wayland applications. These have been mostly implemented manually, since gl_generator lacks support for these newer EGL extensions (except for `EGL_KHR_image_base`). The following extensions were added: - EGL_KHR_image_base - EGL_WL_bind_wayland_display - EGL_WL_create_wayland_buffer_from_image This also exposes EGL and GLX raw API calls on EGL and GLX displays. --- CHANGELOG.md | 1 + glutin/src/api/egl/display.rs | 5 + glutin/src/api/egl/mod.rs | 9 +- glutin/src/api/glx/display.rs | 5 + glutin/src/api/glx/mod.rs | 4 +- glutin/src/lib_loading.rs | 1 + glutin_egl_sys/build.rs | 1 + glutin_egl_sys/src/egl.rs | 252 ++++++++++++++++++ glutin_egl_sys/src/lib.rs | 32 +-- glutin_examples/examples/egl_device.rs | 2 +- .../examples/switch_render_thread.rs | 4 +- glutin_examples/src/lib.rs | 2 +- 12 files changed, 281 insertions(+), 37 deletions(-) create mode 100644 glutin_egl_sys/src/egl.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 98f44731b4..4de13ca47a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - **Breaking:** updated `raw-window-handle` dependency to `0.6`. - Bump MSRV from `1.65` to `1.70`. - Bump `windows-sys` from `0.48.0` to `0.52.0`. +- Expose `Egl` and `Glx` raw API functions on `Egl` and `Glx` displays. # Version 0.31.3 diff --git a/glutin/src/api/egl/display.rs b/glutin/src/api/egl/display.rs index 6801f94d96..0cae10d9b4 100644 --- a/glutin/src/api/egl/display.rs +++ b/glutin/src/api/egl/display.rs @@ -214,6 +214,11 @@ impl Display { Device::from_ptr(self.inner.egl, device) } + /// Get a reference to the initialized EGL API. + pub fn egl(&self) -> &'static Egl { + self.inner.egl + } + /// Terminate the EGL display. /// /// When the display is managed by glutin with the diff --git a/glutin/src/api/egl/mod.rs b/glutin/src/api/egl/mod.rs index c39cc16ed1..a8c9e940f5 100644 --- a/glutin/src/api/egl/mod.rs +++ b/glutin/src/api/egl/mod.rs @@ -41,7 +41,9 @@ pub(crate) static EGL: Lazy> = Lazy::new(|| { type EglGetProcAddress = unsafe extern "C" fn(*const ffi::c_void) -> *const ffi::c_void; static EGL_GET_PROC_ADDRESS: OnceCell> = OnceCell::new(); -pub(crate) struct Egl(pub SymWrapper); +/// EGL interface. +#[allow(missing_debug_implementations)] +pub struct Egl(SymWrapper); unsafe impl Sync for Egl {} unsafe impl Send for Egl {} @@ -68,6 +70,11 @@ impl SymLoading for egl::Egl { } }; + egl::BindWaylandDisplayWL::load_with(loader); + egl::UnbindWaylandDisplayWL::load_with(loader); + egl::QueryWaylandBufferWL::load_with(loader); + egl::CreateWaylandBufferFromImageWL::load_with(loader); + Self::load_with(loader) } } diff --git a/glutin/src/api/glx/display.rs b/glutin/src/api/glx/display.rs index 8b52dfba80..6162afbae7 100644 --- a/glutin/src/api/glx/display.rs +++ b/glutin/src/api/glx/display.rs @@ -110,6 +110,11 @@ impl Display { Ok(Self { inner }) } + /// Get a reference to the initialized GLX API. + pub fn glx(&self) -> &'static Glx { + self.inner.glx + } + fn extract_display_features( extensions: &HashSet<&'static str>, version: Version, diff --git a/glutin/src/api/glx/mod.rs b/glutin/src/api/glx/mod.rs index d90b0f5095..ccaba62974 100644 --- a/glutin/src/api/glx/mod.rs +++ b/glutin/src/api/glx/mod.rs @@ -58,7 +58,9 @@ static GLX_EXTRA: Lazy> = Lazy::new(|| { Some(GlxExtra::new(glx)) }); -pub(crate) struct Glx(pub SymWrapper); +/// GLX interface. +#[allow(missing_debug_implementations)] +pub struct Glx(pub SymWrapper); unsafe impl Sync for Glx {} unsafe impl Send for Glx {} diff --git a/glutin/src/lib_loading.rs b/glutin/src/lib_loading.rs index 679b5cd44d..c1f9985bb2 100644 --- a/glutin/src/lib_loading.rs +++ b/glutin/src/lib_loading.rs @@ -15,6 +15,7 @@ pub trait SymLoading { } #[derive(Clone)] +#[allow(missing_debug_implementations)] pub struct SymWrapper { sym: T, _lib: Arc, diff --git a/glutin_egl_sys/build.rs b/glutin_egl_sys/build.rs index 337458bd61..c8dbedf87a 100644 --- a/glutin_egl_sys/build.rs +++ b/glutin_egl_sys/build.rs @@ -39,6 +39,7 @@ fn main() { "EGL_KHR_create_context_no_error", "EGL_KHR_display_reference", "EGL_KHR_fence_sync", + "EGL_KHR_image_base", "EGL_KHR_platform_android", "EGL_KHR_platform_gbm", "EGL_KHR_platform_wayland", diff --git a/glutin_egl_sys/src/egl.rs b/glutin_egl_sys/src/egl.rs new file mode 100644 index 0000000000..8d8a2bc0c5 --- /dev/null +++ b/glutin_egl_sys/src/egl.rs @@ -0,0 +1,252 @@ +//! Manual implementation of EGL bindings. +//! +//! This is necessary since `gl_generator` is unmaintaned and incapable of +//! generating bindings for some of the newer extensions. + +use std::ffi::c_uint; + +pub type khronos_utime_nanoseconds_t = super::khronos_utime_nanoseconds_t; +pub type khronos_uint64_t = super::khronos_uint64_t; +pub type khronos_ssize_t = super::khronos_ssize_t; +pub type EGLNativeDisplayType = super::EGLNativeDisplayType; +pub type EGLNativePixmapType = super::EGLNativePixmapType; +pub type EGLNativeWindowType = super::EGLNativeWindowType; +pub type EGLint = super::EGLint; +pub type NativeDisplayType = super::EGLNativeDisplayType; +pub type NativePixmapType = super::EGLNativePixmapType; +pub type NativeWindowType = super::EGLNativeWindowType; + +include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs")); + +// EGL_EXT_platform_xcb +pub const PLATFORM_XCB_EXT: super::EGLenum = 0x31DC; +pub const PLATFORM_XCB_SCREEN_EXT: super::EGLenum = 0x31DE; +// EGL_EXT_device_query_name +pub const RENDERER_EXT: super::EGLenum = 0x335F; +// EGL_ANGLE_platform_angle - https://chromium.googlesource.com/angle/angle/+/HEAD/extensions/EGL_ANGLE_platform_angle.txt +pub const PLATFORM_ANGLE_ANGLE: super::EGLenum = 0x3202; +pub const PLATFORM_ANGLE_TYPE_ANGLE: super::EGLenum = 0x3203; +pub const PLATFORM_ANGLE_TYPE_VULKAN_ANGLE: super::EGLenum = 0x3450; +pub const PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE: super::EGLenum = 0x3204; +pub const PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE: super::EGLenum = 0x3205; +pub const PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED: super::EGLenum = 0x3451; +pub const PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE: super::EGLenum = 0x348F; +pub const PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE: super::EGLenum = 0x3206; +pub const PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE: super::EGLenum = 0x320A; +pub const PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE: super::EGLenum = 0x345E; + +mod wayland_storage { + use super::FnPtr; + use super::__gl_imports::raw; + + // EGL_WL_create_wayland_buffer_from_image + pub static mut CREATE_WAYLAND_BUFFER_FROM_IMAGE_WL: FnPtr = + FnPtr { f: super::missing_fn_panic as *const raw::c_void, is_loaded: false }; + + // EGL_WL_bind_wayland_display + pub static mut BIND_WAYLAND_DISPLAY_WL: FnPtr = + FnPtr { f: super::missing_fn_panic as *const raw::c_void, is_loaded: false }; + pub static mut UNBIND_WAYLAND_DISPLAY_WL: FnPtr = + FnPtr { f: super::missing_fn_panic as *const raw::c_void, is_loaded: false }; + pub static mut QUERY_WAYLAND_BUFFER_WL: FnPtr = + FnPtr { f: super::missing_fn_panic as *const raw::c_void, is_loaded: false }; +} + +impl Egl { + #[allow(non_snake_case, unused_variables, dead_code)] + #[inline] + pub unsafe fn CreateWaylandBufferFromImageWL( + &self, + dpy: types::EGLDisplay, + image: types::EGLImageKHR, + ) -> *mut std::ffi::c_void { + __gl_imports::mem::transmute::< + _, + extern "system" fn(types::EGLDisplay, types::EGLImageKHR) -> *mut std::ffi::c_void, + >(wayland_storage::CREATE_WAYLAND_BUFFER_FROM_IMAGE_WL.f)(dpy, image) + } + + #[allow(non_snake_case, unused_variables, dead_code)] + #[inline] + pub unsafe fn BindWaylandDisplayWL( + &self, + dpy: types::EGLDisplay, + display: *mut __gl_imports::raw::c_void, + ) -> types::EGLBoolean { + __gl_imports::mem::transmute::< + _, + extern "system" fn( + types::EGLDisplay, + *mut __gl_imports::raw::c_void, + ) -> types::EGLBoolean, + >(wayland_storage::BIND_WAYLAND_DISPLAY_WL.f)(dpy, display) + } + + #[allow(non_snake_case, unused_variables, dead_code)] + #[inline] + pub unsafe fn UnbindWaylandDisplayWL( + &self, + dpy: types::EGLDisplay, + display: *mut __gl_imports::raw::c_void, + ) -> types::EGLBoolean { + __gl_imports::mem::transmute::< + _, + extern "system" fn( + types::EGLDisplay, + *mut __gl_imports::raw::c_void, + ) -> types::EGLBoolean, + >(wayland_storage::UNBIND_WAYLAND_DISPLAY_WL.f)(dpy, display) + } + + #[allow(non_snake_case, unused_variables, dead_code)] + #[inline] + pub unsafe fn QueryWaylandBufferWL( + &self, + dpy: types::EGLDisplay, + buffer: *mut __gl_imports::raw::c_void, + attribute: types::EGLint, + value: *mut types::EGLint, + ) -> types::EGLBoolean { + __gl_imports::mem::transmute::< + _, + extern "system" fn( + types::EGLDisplay, + *mut __gl_imports::raw::c_void, + types::EGLint, + *mut types::EGLint, + ) -> types::EGLBoolean, + >(wayland_storage::QUERY_WAYLAND_BUFFER_WL.f)(dpy, buffer, attribute, value) + } +} + +// Extension: EGL_WL_create_wayland_buffer_from_image +// + +#[allow(non_snake_case)] +pub mod CreateWaylandBufferFromImageWL { + use super::__gl_imports::raw; + use super::{metaloadfn, wayland_storage, FnPtr}; + + #[inline] + #[allow(dead_code)] + pub fn is_loaded() -> bool { + unsafe { wayland_storage::CREATE_WAYLAND_BUFFER_FROM_IMAGE_WL.is_loaded } + } + + #[allow(dead_code)] + pub fn load_with(mut loadfn: F) + where + F: FnMut(&'static str) -> *const raw::c_void, + { + unsafe { + wayland_storage::CREATE_WAYLAND_BUFFER_FROM_IMAGE_WL = + FnPtr::new(metaloadfn(&mut loadfn, "eglCreateWaylandBufferFromImageWL", &[])) + } + } +} + +// Extension: EGL_WL_bind_wayland_display +// + +// Accepted as in eglCreateImageKHR. +pub const WAYLAND_BUFFER_WL: c_uint = 0x31D5; +// Accepted in the parameter of eglCreateImageKHR. +pub const WAYLAND_PLANE_WL: c_uint = 0x31D6; +// Possible values for EGL_TEXTURE_FORMAT. +pub const TEXTURE_Y_U_V_WL: i32 = 0x31D7; +pub const TEXTURE_Y_UV_WL: i32 = 0x31D8; +pub const TEXTURE_Y_XUXV_WL: i32 = 0x31D9; +pub const TEXTURE_EXTERNAL_WL: i32 = 0x31DA; +// Accepted in the parameter of eglQueryWaylandBufferWL. +pub const EGL_TEXTURE_FORMAT: i32 = 0x3080; +pub const WAYLAND_Y_INVERTED_WL: i32 = 0x31DB; + +#[allow(non_snake_case)] +pub mod BindWaylandDisplayWL { + use super::__gl_imports::raw; + use super::{metaloadfn, wayland_storage, FnPtr}; + + #[inline] + #[allow(dead_code)] + pub fn is_loaded() -> bool { + unsafe { wayland_storage::BIND_WAYLAND_DISPLAY_WL.is_loaded } + } + + #[allow(dead_code)] + pub fn load_with(mut loadfn: F) + where + F: FnMut(&'static str) -> *const raw::c_void, + { + unsafe { + wayland_storage::BIND_WAYLAND_DISPLAY_WL = + FnPtr::new(metaloadfn(&mut loadfn, "eglBindWaylandDisplayWL", &[])) + } + } +} + +#[allow(non_snake_case)] +pub mod UnbindWaylandDisplayWL { + use super::__gl_imports::raw; + use super::{metaloadfn, wayland_storage, FnPtr}; + + #[inline] + #[allow(dead_code)] + pub fn is_loaded() -> bool { + unsafe { wayland_storage::UNBIND_WAYLAND_DISPLAY_WL.is_loaded } + } + + #[allow(dead_code)] + pub fn load_with(mut loadfn: F) + where + F: FnMut(&'static str) -> *const raw::c_void, + { + unsafe { + wayland_storage::UNBIND_WAYLAND_DISPLAY_WL = + FnPtr::new(metaloadfn(&mut loadfn, "eglUnbindWaylandDisplayWL", &[])) + } + } +} + +#[allow(non_snake_case)] +pub mod QueryWaylandBufferWL { + use super::__gl_imports::raw; + use super::{metaloadfn, wayland_storage, FnPtr}; + + #[inline] + #[allow(dead_code)] + pub fn is_loaded() -> bool { + unsafe { wayland_storage::QUERY_WAYLAND_BUFFER_WL.is_loaded } + } + + #[allow(dead_code)] + pub fn load_with(mut loadfn: F) + where + F: FnMut(&'static str) -> *const raw::c_void, + { + unsafe { + wayland_storage::QUERY_WAYLAND_BUFFER_WL = + FnPtr::new(metaloadfn(&mut loadfn, "eglQueryWaylandBufferWL", &[])) + } + } +} + +/// OpenGL function loader. +/// +/// This is based on the loader generated by `gl_generator`. +#[inline(never)] +fn metaloadfn( + loadfn: &mut dyn FnMut(&'static str) -> *const __gl_imports::raw::c_void, + symbol: &'static str, + fallbacks: &[&'static str], +) -> *const __gl_imports::raw::c_void { + let mut ptr = loadfn(symbol); + if ptr.is_null() { + for &sym in fallbacks { + ptr = loadfn(sym); + if !ptr.is_null() { + break; + } + } + } + ptr +} diff --git a/glutin_egl_sys/src/lib.rs b/glutin_egl_sys/src/lib.rs index cb1dad8e9a..4d8cf7f7fa 100644 --- a/glutin_egl_sys/src/lib.rs +++ b/glutin_egl_sys/src/lib.rs @@ -12,37 +12,7 @@ #![allow(clippy::manual_non_exhaustive)] #![allow(clippy::unnecessary_cast)] -pub mod egl { - pub type khronos_utime_nanoseconds_t = super::khronos_utime_nanoseconds_t; - pub type khronos_uint64_t = super::khronos_uint64_t; - pub type khronos_ssize_t = super::khronos_ssize_t; - pub type EGLNativeDisplayType = super::EGLNativeDisplayType; - pub type EGLNativePixmapType = super::EGLNativePixmapType; - pub type EGLNativeWindowType = super::EGLNativeWindowType; - pub type EGLint = super::EGLint; - pub type NativeDisplayType = super::EGLNativeDisplayType; - pub type NativePixmapType = super::EGLNativePixmapType; - pub type NativeWindowType = super::EGLNativeWindowType; - - include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs")); - - // TODO should upstream these: - // EGL_EXT_platform_xcb - pub const PLATFORM_XCB_EXT: super::EGLenum = 0x31DC; - pub const PLATFORM_XCB_SCREEN_EXT: super::EGLenum = 0x31DE; - // EGL_EXT_device_query_name - pub const RENDERER_EXT: super::EGLenum = 0x335F; - // EGL_ANGLE_platform_angle - https://chromium.googlesource.com/angle/angle/+/HEAD/extensions/EGL_ANGLE_platform_angle.txt - pub const PLATFORM_ANGLE_ANGLE: super::EGLenum = 0x3202; - pub const PLATFORM_ANGLE_TYPE_ANGLE: super::EGLenum = 0x3203; - pub const PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE: super::EGLenum = 0x3204; - pub const PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE: super::EGLenum = 0x3205; - pub const PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED: super::EGLenum = 0x3451; - pub const PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE: super::EGLenum = 0x348F; - pub const PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE: super::EGLenum = 0x3206; - pub const PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE: super::EGLenum = 0x320A; - pub const PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE: super::EGLenum = 0x345E; -} +pub mod egl; pub use self::egl::types::{EGLContext, EGLDisplay}; diff --git a/glutin_examples/examples/egl_device.rs b/glutin_examples/examples/egl_device.rs index 89c9f1a4f6..191eb657eb 100644 --- a/glutin_examples/examples/egl_device.rs +++ b/glutin_examples/examples/egl_device.rs @@ -112,7 +112,7 @@ mod example { } let path = Path::new(IMG_PATH); - let file = OpenOptions::new().write(true).create(true).open(path).unwrap(); + let file = OpenOptions::new().write(true).truncate(true).open(path).unwrap(); let mut encoder = png::Encoder::new(file, 1280, 720); encoder.set_depth(png::BitDepth::Eight); diff --git a/glutin_examples/examples/switch_render_thread.rs b/glutin_examples/examples/switch_render_thread.rs index 8cffb6c26c..822c9ce382 100644 --- a/glutin_examples/examples/switch_render_thread.rs +++ b/glutin_examples/examples/switch_render_thread.rs @@ -8,8 +8,8 @@ use glutin::config::ConfigTemplateBuilder; use glutin::context::{ContextAttributesBuilder, PossiblyCurrentContext}; use glutin::display::GetGlDisplay; use glutin::error::{Error as GlutinError, ErrorKind}; -use glutin::prelude::{NotCurrentGlContext, PossiblyCurrentGlContext, *}; -use glutin::surface::{GlSurface, Surface, WindowSurface}; +use glutin::prelude::*; +use glutin::surface::{Surface, WindowSurface}; use glutin_examples::gl::types::GLfloat; use glutin_examples::{gl_config_picker, Renderer}; use glutin_winit::{self, DisplayBuilder, GlWindow}; diff --git a/glutin_examples/src/lib.rs b/glutin_examples/src/lib.rs index 3bf16f223d..4763cf670e 100644 --- a/glutin_examples/src/lib.rs +++ b/glutin_examples/src/lib.rs @@ -18,7 +18,7 @@ use glutin::display::GetGlDisplay; use glutin::prelude::*; use glutin::surface::{Surface, SwapInterval, WindowSurface}; -use glutin_winit::{self, DisplayBuilder, GlWindow}; +use glutin_winit::{DisplayBuilder, GlWindow}; pub mod gl { #![allow(clippy::all)]