diff --git a/.gitignore b/.gitignore index 0584bb40fb..4461cbc942 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,3 @@ target/ .DS_Store *~ #*# -headless.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 020da65cbb..fd100c930e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +- Added `EGLDevice` wrappers for EGL. +- Added EGL dependent api to make a context current without a surface. - Added `supports_transparency` on `GlConfig`. - On GLX, try all extensions when setting vsync. - On WGL, fixed that `Surface::swap_buffers` takes longer with every call caused by frequent calls of the win32 function `HDC GetDC(HWND hWnd)`. diff --git a/glutin/src/api/egl/config.rs b/glutin/src/api/egl/config.rs index bf03db28f7..2d786c93a4 100644 --- a/glutin/src/api/egl/config.rs +++ b/glutin/src/api/egl/config.rs @@ -308,7 +308,7 @@ impl GlConfig for Config { #[cfg(any(wayland_platform, x11_platform))] fn supports_transparency(&self) -> Option { use raw_window_handle::RawDisplayHandle; - match *self.inner.display.inner._native_display { + match *self.inner.display.inner._native_display? { #[cfg(x11_platform)] RawDisplayHandle::Xlib(_) | RawDisplayHandle::Xcb(_) => { self.x11_visual().map(|visual| visual.supports_transparency()) @@ -356,7 +356,7 @@ impl AsRawConfig for Config { #[cfg(x11_platform)] impl X11GlConfigExt for Config { fn x11_visual(&self) -> Option { - match *self.inner.display.inner._native_display { + match *self.inner.display.inner._native_display? { raw_window_handle::RawDisplayHandle::Xlib(display_handle) => unsafe { let xid = self.native_visual(); X11VisualInfo::from_xid(display_handle.display as *mut _, xid as _) diff --git a/glutin/src/api/egl/context.rs b/glutin/src/api/egl/context.rs index ea534cec00..e45cddd560 100644 --- a/glutin/src/api/egl/context.rs +++ b/glutin/src/api/egl/context.rs @@ -162,6 +162,13 @@ pub struct NotCurrentContext { } impl NotCurrentContext { + /// Make a [`Self::PossiblyCurrentContext`] indicating that the context + /// could be current on the thread. + pub fn make_current_surfaceless(self) -> Result { + self.inner.make_current_surfaceless()?; + Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) + } + fn new(inner: ContextInner) -> Self { Self { inner } } @@ -225,6 +232,13 @@ pub struct PossiblyCurrentContext { _nosendsync: PhantomData, } +impl PossiblyCurrentContext { + /// Make this context current on the calling thread. + pub fn make_current_surfaceless(&self) -> Result<()> { + self.inner.make_current_surfaceless() + } +} + impl PossiblyCurrentGlContext for PossiblyCurrentContext { type NotCurrentContext = NotCurrentContext; @@ -289,6 +303,22 @@ pub(crate) struct ContextInner { } impl ContextInner { + fn make_current_surfaceless(&self) -> Result<()> { + unsafe { + if self.display.inner.egl.MakeCurrent( + *self.display.inner.raw, + egl::NO_SURFACE, + egl::NO_SURFACE, + *self.raw, + ) == egl::FALSE + { + super::check_error() + } else { + Ok(()) + } + } + } + fn make_current_draw_read( &self, surface_draw: &Surface, diff --git a/glutin/src/api/egl/device.rs b/glutin/src/api/egl/device.rs new file mode 100644 index 0000000000..465fb6050c --- /dev/null +++ b/glutin/src/api/egl/device.rs @@ -0,0 +1,157 @@ +//! Everything related to `EGLDevice`. + +use std::collections::HashSet; +use std::ffi::{c_void, CStr}; +use std::ptr; + +use glutin_egl_sys::egl; +use glutin_egl_sys::egl::types::EGLDeviceEXT; + +use crate::error::{ErrorKind, Result}; + +use super::display::{extensions_from_ptr, get_extensions, NO_DISPLAY_EXTENSIONS}; +use super::{Egl, EGL}; + +/// Wrapper for `EGLDevice`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Device { + inner: EGLDeviceEXT, + extensions: HashSet<&'static str>, + name: Option, + vendor: Option, +} + +impl Device { + /// Query the available devices. + /// + /// This function returns [`Err`] if the `EGL_EXT_device_query` and + /// `EGL_EXT_device_enumeration` or `EGL_EXT_device_base` extensions are + /// not available. + pub fn query_devices() -> Result> { + let egl = match EGL.as_ref() { + Some(egl) => egl, + None => return Err(ErrorKind::NotFound.into()), + }; + + let no_display_extensions = + NO_DISPLAY_EXTENSIONS.get_or_init(|| get_extensions(egl, egl::NO_DISPLAY)); + + // Querying devices requires EGL_EXT_device_enumeration and + // EGL_EXT_device_query. + // + // Or we can check for the EGL_EXT_device_base extension since it contains both + // extensions. + if (!no_display_extensions.contains("EGL_EXT_device_enumeration") + && !no_display_extensions.contains("EGL_EXT_device_query")) + || !no_display_extensions.contains("EGL_EXT_device_base") + { + return Err(ErrorKind::NotSupported("EGL does not support EGL_EXT_device_base").into()); + } + + let mut device_count = 0; + + if unsafe { + // The specification states: + // > An EGL_BAD_PARAMETER error is generated if is + // > less than or equal to zero unless is NULL, or if + // > is NULL. + // + // The error will never be generated since num_devices is a pointer + // to the count being queried. Therefore there is no need to check + // the error. + egl.QueryDevicesEXT(0, ptr::null_mut(), &mut device_count) == egl::FALSE + } { + super::check_error()?; + // On failure, EGL_FALSE is returned. + return Err(ErrorKind::NotSupported("Querying device count failed").into()); + } + + let mut devices = Vec::with_capacity(device_count as usize); + + unsafe { + let mut count = device_count; + if egl.QueryDevicesEXT(device_count, devices.as_mut_ptr(), &mut count) == egl::FALSE { + super::check_error()?; + // On failure, EGL_FALSE is returned. + return Err(ErrorKind::NotSupported("Querying devices failed").into()); + } + + // SAFETY: EGL has initialized the vector for the number of devices. + devices.set_len(device_count as usize); + } + + Ok(devices.into_iter().flat_map(|ptr| Device::from_ptr(egl, ptr))) + } + + /// Get the device extensions supported by this device. + /// + /// These extensions are distinct from the display extensions and should not + /// be used interchangeably. + pub fn extensions(&self) -> &HashSet<&str> { + &self.extensions + } + + /// Get the name of the device. + /// + /// This function will return [`None`] if the `EGL_EXT_device_query_name` + /// device extension is not available. + pub fn name(&self) -> Option<&str> { + self.name.as_deref() + } + + /// Get the vendor of the device. + /// + /// This function will return [`None`] if the `EGL_EXT_device_query_name` + /// device extension is not available. + pub fn vendor(&self) -> Option<&str> { + self.vendor.as_deref() + } + + /// Get a raw handle to the `EGLDevice`. + pub fn raw_device(&self) -> *const c_void { + self.inner + } +} + +// SAFETY: An EGLDevice is immutable and valid for the lifetime of the EGL +// library. +unsafe impl Send for Device {} +unsafe impl Sync for Device {} + +impl Device { + unsafe fn query_string(egl_device: *const c_void, name: egl::types::EGLenum) -> Option { + let egl = super::EGL.as_ref().unwrap(); + + // SAFETY: The caller has ensured the name is valid. + let ptr = unsafe { egl.QueryDeviceStringEXT(egl_device, name as _) }; + + if ptr.is_null() { + return None; + } + + unsafe { CStr::from_ptr(ptr) }.to_str().ok().map(String::from) + } + + pub(crate) fn from_ptr(egl: &Egl, ptr: *const c_void) -> Result { + // SAFETY: The EGL specification guarantees the returned string is + // static and null terminated: + // + // > eglQueryDeviceStringEXT returns a pointer to a static, + // > zero-terminated string describing some aspect of the specified + // > EGLDeviceEXT. must be EGL_EXTENSIONS. + let extensions = + unsafe { extensions_from_ptr(egl.QueryDeviceStringEXT(ptr, egl::EXTENSIONS as _)) }; + + let (name, vendor) = if extensions.contains("EGL_EXT_device_query_name") { + // SAFETY: RENDERER_EXT and VENDOR are valid strings for device string queries + // if EGL_EXT_device_query_name. + unsafe { + (Self::query_string(ptr, egl::RENDERER_EXT), Self::query_string(ptr, egl::VENDOR)) + } + } else { + (None, None) + }; + + Ok(Self { inner: ptr, extensions, name, vendor }) + } +} diff --git a/glutin/src/api/egl/display.rs b/glutin/src/api/egl/display.rs index 83ef46d08c..668c3efba1 100644 --- a/glutin/src/api/egl/display.rs +++ b/glutin/src/api/egl/display.rs @@ -2,9 +2,10 @@ use std::collections::HashSet; use std::ffi::{self, CStr}; -use std::fmt; use std::ops::Deref; +use std::os::raw::c_char; use std::sync::Arc; +use std::{fmt, ptr}; use glutin_egl_sys::egl; use glutin_egl_sys::egl::types::{EGLAttrib, EGLDisplay, EGLint}; @@ -23,12 +24,13 @@ use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSur use super::config::Config; use super::context::NotCurrentContext; +use super::device::Device; use super::surface::Surface; use super::{Egl, EGL}; /// Extensions that don't require any display. -static NO_DISPLAY_EXTENSIONS: OnceCell> = OnceCell::new(); +pub(crate) static NO_DISPLAY_EXTENSIONS: OnceCell> = OnceCell::new(); /// A wrapper for the `EGLDisplay` and its supported extensions. #[derive(Debug, Clone)] @@ -72,28 +74,113 @@ impl Display { } })?; - let version = unsafe { - let (mut major, mut minor) = (0, 0); - if egl.Initialize(display, &mut major, &mut minor) == egl::FALSE { - return Err(super::check_error().err().unwrap()); - } + Self::initialize_display(egl, display) + } - Version::new(major as u8, minor as u8) + /// Create an EGL display using the specified device. + /// + /// In most cases, prefer [`Display::new`] unless you need to render + /// off screen or use other extensions like EGLStreams. + /// + /// This function may take an optional [`RawDisplayHandle`] argument. At the + /// moment the `raw_display` argument is ignored and this function will + /// return [`Err`]. This may change in the future. + /// + /// # Safety + /// + /// If `raw_display` is [`Some`], `raw_display` must point to a valid system + /// display. + pub unsafe fn with_device( + device: &Device, + raw_display: Option, + ) -> Result { + let egl = match EGL.as_ref() { + Some(egl) => egl, + None => return Err(ErrorKind::NotFound.into()), }; - // Load extensions. - let client_extensions = get_extensions(egl, display); - let features = Self::extract_display_features(&client_extensions, version); + if raw_display.is_some() { + return Err(ErrorKind::NotSupported( + "Display::with_device does not support a `raw_display` argument yet", + ) + .into()); + } - let inner = Arc::new(DisplayInner { - egl, - raw: EglDisplay(display), - _native_display: NativeDisplay(raw_display), - version, - features, - client_extensions, - }); - Ok(Self { inner }) + if !egl.GetPlatformDisplayEXT.is_loaded() { + return Err(ErrorKind::NotSupported("eglGetPlatformDisplayEXT is not supported").into()); + } + + // Okay to unwrap here because the client extensions must have been enumerated + // while querying the available devices or the device was gotten from an + // existing display. + let extensions = NO_DISPLAY_EXTENSIONS.get().unwrap(); + + if !extensions.contains("EGL_EXT_platform_base") + && !extensions.contains("EGL_EXT_platform_device") + { + return Err(ErrorKind::NotSupported( + "Creating a display from a device is not supported", + ) + .into()); + } + + let mut attrs = Vec::::with_capacity(2); + + // TODO: Some extensions exist like EGL_EXT_device_drm which allow specifying + // which DRM master fd to use under the hood by the implementation. This would + // mean there would need to be an unsafe equivalent to this function. + + // Push `egl::NONE` to terminate the list. + attrs.push(egl::NONE as EGLint); + + let display = Self::check_display_error(unsafe { + egl.GetPlatformDisplayEXT( + egl::PLATFORM_DEVICE_EXT, + device.raw_device() as *mut _, + attrs.as_ptr(), + ) + })?; + + Self::initialize_display(egl, display) + } + + /// Get the [`Device`] the display is using. + /// + /// This function returns [`Err`] if the `EGL_EXT_device_query` or + /// `EGL_EXT_device_base` extensions are not available. + pub fn device(&self) -> Result { + let no_display_extensions = NO_DISPLAY_EXTENSIONS.get().unwrap(); + + // Querying the device of a display only requires EGL_EXT_device_query, but we + // also check if EGL_EXT_device_base is available since + // EGL_EXT_device_base also provides EGL_EXT_device_query. + if !no_display_extensions.contains("EGL_EXT_device_query") + || !no_display_extensions.contains("EGL_EXT_device_base") + { + return Err(ErrorKind::NotSupported( + "Querying the device from a display is not supported", + ) + .into()); + } + + let device = ptr::null_mut(); + if unsafe { + self.inner.egl.QueryDisplayAttribEXT( + self.inner.raw.0, + egl::DEVICE_EXT as EGLint, + device as *mut _, + ) + } == egl::FALSE + { + // Check for EGL_NOT_INITIALIZED in case the display was externally terminated. + // + // EGL_BAD_ATTRIBUTE shouldn't be returned since EGL_DEVICE_EXT should be a + // valid display attribute. + super::check_error()?; + return Err(ErrorKind::NotSupported("Failed to query device from display").into()); + } + + Device::from_ptr(self.inner.egl, device) } fn get_platform_display(egl: &Egl, display: RawDisplayHandle) -> Result { @@ -257,6 +344,31 @@ impl Display { Ok(display) } } + + fn initialize_display(egl: &'static Egl, display: EGLDisplay) -> Result { + let version = unsafe { + let (mut major, mut minor) = (0, 0); + if egl.Initialize(display, &mut major, &mut minor) == egl::FALSE { + return Err(super::check_error().err().unwrap()); + } + + Version::new(major as u8, minor as u8) + }; + + // Load extensions. + let client_extensions = get_extensions(egl, display); + let features = Self::extract_display_features(&client_extensions, version); + + let inner = Arc::new(DisplayInner { + egl, + raw: EglDisplay(display), + _native_display: None, + version, + client_extensions, + features, + }); + Ok(Self { inner }) + } } impl GlDisplay for Display { @@ -349,7 +461,7 @@ pub(crate) struct DisplayInner { pub(crate) features: DisplayFeatures, /// The raw display used to create EGL display. - pub(crate) _native_display: NativeDisplay, + pub(crate) _native_display: Option, } impl fmt::Debug for DisplayInner { @@ -450,17 +562,32 @@ impl Deref for EglDisplay { } /// Collect EGL extensions for the given `display`. -fn get_extensions(egl: &Egl, display: EGLDisplay) -> HashSet<&'static str> { +pub(crate) fn get_extensions(egl: &Egl, display: EGLDisplay) -> HashSet<&'static str> { unsafe { let extensions = egl.QueryString(display, egl::EXTENSIONS as i32); - if extensions.is_null() { - return HashSet::new(); - } + // SAFETY: The EGL specification guarantees the returned string is + // static and null terminated: + // + // > eglQueryString returns a pointer to a static, zero-terminated + // > string describing properties of the EGL client or of an EGL + // > display connection. + extensions_from_ptr(extensions) + } +} - if let Ok(extensions) = CStr::from_ptr(extensions).to_str() { - extensions.split(' ').collect::>() - } else { - HashSet::new() - } +/// # Safety +/// +/// - The `extensions` pointer must be NULL (representing no extensions) or it +/// must be non-null and contain a static, null terminated C string. +pub(crate) unsafe fn extensions_from_ptr(extensions: *const c_char) -> HashSet<&'static str> { + if extensions.is_null() { + return HashSet::new(); + } + + // SAFETY: The caller has ensured the string pointer is null terminated. + if let Ok(extensions) = unsafe { CStr::from_ptr(extensions) }.to_str() { + extensions.split(' ').collect::>() + } else { + HashSet::new() } } diff --git a/glutin/src/api/egl/mod.rs b/glutin/src/api/egl/mod.rs index e7605c6ca7..c39cc16ed1 100644 --- a/glutin/src/api/egl/mod.rs +++ b/glutin/src/api/egl/mod.rs @@ -1,4 +1,10 @@ //! EGL platform Api. +//! +//! This platform is typically available on Linux, Android and other Unix-like +//! platforms. +//! +//! The EGL platform allows creating a [`Display`](self::display::Display) from +//! a [`Device`](self::device::Device). use std::ffi::{self, CString}; use std::ops::{Deref, DerefMut}; @@ -18,6 +24,7 @@ use crate::lib_loading::{SymLoading, SymWrapper}; pub mod config; pub mod context; +pub mod device; pub mod display; pub mod surface; diff --git a/glutin_egl_sys/build.rs b/glutin_egl_sys/build.rs index 594dcc2096..ccef77db20 100644 --- a/glutin_egl_sys/build.rs +++ b/glutin_egl_sys/build.rs @@ -28,6 +28,7 @@ fn main() { "EGL_EXT_device_drm_render_node", "EGL_EXT_device_enumeration", "EGL_EXT_device_query", + "EGL_EXT_device_query_name", "EGL_EXT_pixel_format_float", "EGL_EXT_platform_base", "EGL_EXT_platform_device", diff --git a/glutin_egl_sys/src/lib.rs b/glutin_egl_sys/src/lib.rs index d291001cb5..bb5d019f41 100644 --- a/glutin_egl_sys/src/lib.rs +++ b/glutin_egl_sys/src/lib.rs @@ -26,9 +26,12 @@ pub mod egl { include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs")); - // TODO should upstream this. + // TODO should upstream these: + // EGL_EXT_platform_xcb pub const PLATFORM_XCB_EXT: super::EGLenum = 0x31DC; pub const PLATFORM_XCB_SCREEN_EXT: super::EGLenum = 0x31DC; + // EGL_EXT_device_query_name + pub const RENDERER_EXT: super::EGLenum = 0x335F; } pub use self::egl::types::{EGLContext, EGLDisplay}; diff --git a/glutin_examples/Cargo.toml b/glutin_examples/Cargo.toml index d426da0a49..3c17706c9c 100644 --- a/glutin_examples/Cargo.toml +++ b/glutin_examples/Cargo.toml @@ -12,7 +12,7 @@ publish = false [features] default = ["egl", "glx", "x11", "wayland", "wgl"] -egl = ["glutin/egl"] +egl = ["glutin/egl", "png"] glx = ["glutin/glx", "glutin/x11", "winit/x11", "x11"] wgl = ["glutin/wgl"] x11 = ["glutin/x11", "winit/x11"] @@ -22,6 +22,7 @@ wayland = ["glutin/wayland", "winit/wayland", "winit/wayland-dlopen", "winit/way glutin = { path = "../glutin", default-features = false } winit = { version = "0.27.2", default-features = false } raw-window-handle = "0.5.0" +png = { version = "0.17.6", optional = true } [target.'cfg(target_os = "android")'.dependencies] ndk-glue = "0.7" # Keep in sync with winit dependency @@ -33,3 +34,7 @@ cfg_aliases = "0.1.1" [[example]] name = "android" crate-type = ["cdylib"] + +[[example]] +name = "egl_device" +required-features = ["egl"] diff --git a/glutin_examples/examples/egl_device.rs b/glutin_examples/examples/egl_device.rs new file mode 100644 index 0000000000..86189f9a57 --- /dev/null +++ b/glutin_examples/examples/egl_device.rs @@ -0,0 +1,142 @@ +fn main() { + #[cfg(egl_backend)] + example::run(); +} + +#[cfg(egl_backend)] +mod example { + use std::fs::OpenOptions; + use std::path::Path; + + use glutin::api::egl::device::Device; + use glutin::api::egl::display::Display; + use glutin::config::{ConfigSurfaceTypes, ConfigTemplate, ConfigTemplateBuilder}; + use glutin::context::{ContextApi, ContextAttributesBuilder}; + use glutin::prelude::*; + use glutin_examples::{gl, Renderer}; + + const IMG_PATH: &str = concat!(env!("OUT_DIR"), "/egl_device.png"); + + pub fn run() { + let devices = Device::query_devices().expect("Failed to query devices").collect::>(); + + for (index, device) in devices.iter().enumerate() { + println!( + "Device {}: Name: {} Vendor: {}", + index, + device.name().unwrap_or("UNKNOWN"), + device.vendor().unwrap_or("UNKNOWN") + ); + } + + let device = devices.first().expect("No available devices"); + + // Create a display using the device. + let display = + unsafe { Display::with_device(device, None) }.expect("Failed to create display"); + + let template = config_template(); + let config = unsafe { display.find_configs(template) } + .unwrap() + .reduce( + |config, acc| { + if config.num_samples() > acc.num_samples() { + config + } else { + acc + } + }, + ) + .expect("No available configs"); + + println!("Picked a config with {} samples", config.num_samples()); + + // Context creation. + // + // In particular, since we are doing offscreen rendering we have no raw window + // handle to provide. + let context_attributes = ContextAttributesBuilder::new().build(None); + + // Since glutin by default tries to create OpenGL core context, which may not be + // present we should try gles. + let fallback_context_attributes = + ContextAttributesBuilder::new().with_context_api(ContextApi::Gles(None)).build(None); + + let not_current = unsafe { + display.create_context(&config, &context_attributes).unwrap_or_else(|_| { + display + .create_context(&config, &fallback_context_attributes) + .expect("failed to create context") + }) + }; + + // Make the context current for rendering + let _context = not_current.make_current_surfaceless().unwrap(); + let renderer = Renderer::new(&display); + + // Create a framebuffer for offscreen rendering since we do not have a window. + let mut framebuffer = 0; + let mut renderbuffer = 0; + unsafe { + renderer.GenFramebuffers(1, &mut framebuffer); + renderer.GenRenderbuffers(1, &mut renderbuffer); + renderer.BindFramebuffer(gl::FRAMEBUFFER, framebuffer); + renderer.BindRenderbuffer(gl::RENDERBUFFER, renderbuffer); + renderer.RenderbufferStorage(gl::RENDERBUFFER, gl::RGBA, 1280, 720); + renderer.FramebufferRenderbuffer( + gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::RENDERBUFFER, + renderbuffer, + ); + } + + renderer.resize(1280, 720); + renderer.draw(); + + let mut buffer = Vec::::with_capacity(1280 * 720 * 4); + unsafe { + // Wait for the previous commands to finish before reading from the framebuffer. + renderer.Finish(); + // Download the framebuffer contents to the buffer. + renderer.ReadPixels( + 0, + 0, + 1280, + 720, + gl::RGBA, + gl::UNSIGNED_BYTE, + buffer.as_mut_ptr() as *mut _, + ); + buffer.set_len(1280 * 720 * 4); + } + + let path = Path::new(IMG_PATH); + let file = OpenOptions::new().write(true).create(true).open(&path).unwrap(); + + let mut encoder = png::Encoder::new(file, 1280, 720); + encoder.set_depth(png::BitDepth::Eight); + encoder.set_color(png::ColorType::Rgba); + let mut png_writer = encoder.write_header().unwrap(); + + png_writer.write_image_data(&buffer[..]).unwrap(); + png_writer.finish().unwrap(); + println!("Output rendered to: {}", path.display()); + + unsafe { + // Unbind the framebuffer and renderbuffer before deleting. + renderer.BindFramebuffer(gl::DRAW_FRAMEBUFFER, 0); + renderer.BindRenderbuffer(gl::RENDERBUFFER, 0); + renderer.DeleteFramebuffers(1, &framebuffer); + renderer.DeleteRenderbuffers(1, &renderbuffer); + } + } + + fn config_template() -> ConfigTemplate { + ConfigTemplateBuilder::default() + .with_alpha_size(8) + // Offscreen rendering has no support window surface support. + .with_surface_type(ConfigSurfaceTypes::empty()) + .build() + } +} diff --git a/glutin_examples/src/lib.rs b/glutin_examples/src/lib.rs index 1647b1773c..9fb9f4faf0 100644 --- a/glutin_examples/src/lib.rs +++ b/glutin_examples/src/lib.rs @@ -4,6 +4,7 @@ use std::ffi::{CStr, CString}; use std::num::NonZeroU32; +use std::ops::Deref; use raw_window_handle::{ HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, @@ -294,7 +295,7 @@ pub struct Renderer { } impl Renderer { - pub fn new(gl_display: &Display) -> Self { + pub fn new(gl_display: &D) -> Self { unsafe { let gl = gl::Gl::load_with(|symbol| { let symbol = CString::new(symbol).unwrap(); @@ -386,6 +387,14 @@ impl Renderer { } } +impl Deref for Renderer { + type Target = gl::Gl; + + fn deref(&self) -> &Self::Target { + &self.gl + } +} + impl Drop for Renderer { fn drop(&mut self) { unsafe {