From 4aa07977ae5b3da6637f55e185e5d069314bd549 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Wed, 8 Nov 2023 15:34:21 +0100 Subject: [PATCH] EGL: Provide color-space support --- glutin-winit/src/lib.rs | 6 +- glutin/src/api/cgl/config.rs | 8 +- glutin/src/api/egl/config.rs | 11 +- glutin/src/api/egl/display.rs | 12 +- glutin/src/api/egl/surface.rs | 207 +++++++++++++++++++++++++++++----- glutin/src/api/glx/config.rs | 20 ++-- glutin/src/api/glx/surface.rs | 12 +- glutin/src/api/wgl/config.rs | 25 ++-- glutin/src/config.rs | 12 +- glutin/src/context.rs | 4 +- glutin/src/display.rs | 14 ++- glutin/src/surface.rs | 17 ++- glutin_egl_sys/build.rs | 73 +++++++----- glutin_examples/src/lib.rs | 55 +++++++-- glutin_glx_sys/build.rs | 38 ++++--- glutin_wgl_sys/build.rs | 38 ++++--- 16 files changed, 404 insertions(+), 148 deletions(-) diff --git a/glutin-winit/src/lib.rs b/glutin-winit/src/lib.rs index 00b9a8f23d..c4281e9956 100644 --- a/glutin-winit/src/lib.rs +++ b/glutin-winit/src/lib.rs @@ -91,9 +91,9 @@ impl DisplayBuilder { /// /// # Api-specific /// - /// **WGL:** - [`WindowAttributes`] **must** be passed in - /// [`Self::with_window_attributes()`] if modern OpenGL(ES) is desired, - /// otherwise only builtin functions like `glClear` will be available. + /// - **WGL:** [`WindowAttributes`] **must** be passed in + /// [`Self::with_window_attributes()`] if modern OpenGL(ES) is desired, + /// otherwise only builtin functions like `glClear()` will be available. pub fn build( mut self, event_loop: &impl GlutinEventLoop, diff --git a/glutin/src/api/cgl/config.rs b/glutin/src/api/cgl/config.rs index 08426def3c..0c1044390f 100644 --- a/glutin/src/api/cgl/config.rs +++ b/glutin/src/api/cgl/config.rs @@ -136,16 +136,16 @@ pub struct Config { impl Config { fn raw_attribute(&self, attrib: NSOpenGLPixelFormatAttribute) -> i32 { + let mut value = 0; unsafe { - let mut value = 0; self.inner.raw.getValues_forAttribute_forVirtualScreen( &mut value, attrib, // They do differ per monitor and require context. Which is kind of insane, but // whatever. Zero is a primary monitor. 0, - ); - value - } + ) + }; + value } #[allow(deprecated)] diff --git a/glutin/src/api/egl/config.rs b/glutin/src/api/egl/config.rs index ecbdea9d4e..7712dc1604 100644 --- a/glutin/src/api/egl/config.rs +++ b/glutin/src/api/egl/config.rs @@ -241,16 +241,19 @@ impl Config { /// /// The caller must ensure that the attribute could be present. unsafe fn raw_attribute(&self, attr: EGLint) -> EGLint { - unsafe { - let mut val = 0; + let mut val = 0; + let success = unsafe { self.inner.display.inner.egl.GetConfigAttrib( *self.inner.display.inner.raw, *self.inner.raw, attr, &mut val, - ); - val as EGLint + ) + }; + if success != 1 { + eprintln!("Could not read Attrib {attr:#0x} from {:?}", self) } + val as EGLint } } diff --git a/glutin/src/api/egl/display.rs b/glutin/src/api/egl/display.rs index 0cae10d9b4..07462a8a56 100644 --- a/glutin/src/api/egl/display.rs +++ b/glutin/src/api/egl/display.rs @@ -28,6 +28,8 @@ use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSur use super::config::Config; use super::context::NotCurrentContext; use super::device::Device; +#[cfg(doc)] +use super::surface::ColorSpace; use super::surface::Surface; use super::{Egl, EGL}; @@ -515,12 +517,15 @@ impl GlDisplay for Display { unsafe { Self::find_configs(self, template) } } + /// Creates a window surface without [`ColorSpace`]. Use + /// [`Self::create_window_surface()`] if you wish to specify a color + /// space. unsafe fn create_window_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { - unsafe { Self::create_window_surface(self, config, surface_attributes) } + unsafe { Self::create_window_surface(self, config, &surface_attributes.clone().into()) } } unsafe fn create_pbuffer_surface( @@ -539,12 +544,15 @@ impl GlDisplay for Display { unsafe { Self::create_context(self, config, context_attributes) } } + /// Creates a pixmap surface without [`ColorSpace`]. Use + /// [`Self::create_pixmap_surface()`] if you wish to specify a color + /// space. unsafe fn create_pixmap_surface( &self, config: &Self::Config, surface_attributes: &SurfaceAttributes, ) -> Result { - unsafe { Self::create_pixmap_surface(self, config, surface_attributes) } + unsafe { Self::create_pixmap_surface(self, config, &surface_attributes.clone().into()) } } fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void { diff --git a/glutin/src/api/egl/surface.rs b/glutin/src/api/egl/surface.rs index 800d911c01..45cc39f3b3 100644 --- a/glutin/src/api/egl/surface.rs +++ b/glutin/src/api/egl/surface.rs @@ -2,16 +2,20 @@ use std::marker::PhantomData; use std::num::NonZeroU32; +use std::ops::Deref; use std::{ffi, fmt}; -use glutin_egl_sys::egl; -use glutin_egl_sys::egl::types::{EGLAttrib, EGLSurface, EGLint}; +use glutin_egl_sys::egl::types::{EGLAttrib, EGLSurface}; +use glutin_egl_sys::egl::{self}; +use glutin_egl_sys::{EGLenum, EGLint}; use raw_window_handle::RawWindowHandle; #[cfg(wayland_platform)] use wayland_sys::{egl::*, ffi_dispatch}; use crate::api::egl::display::EglDisplay; use crate::config::GetGlConfig; +#[cfg(doc)] +use crate::display::GetDisplayExtensions; use crate::display::GetGlDisplay; use crate::error::{ErrorKind, Result}; use crate::prelude::*; @@ -28,6 +32,124 @@ use super::display::Display; /// Hint for the attribute list size. const ATTR_SIZE_HINT: usize = 8; +/// Missing `EGL_EXT_gl_colorspace_bt2020_hlg` constant defined at +pub const EGL_GL_COLORSPACE_BT2020_HLG_EXT: EGLenum = 0x3540; +/// Missing `EXT_gl_colorspace_display_p3_passthrough` constant defined at +pub const EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT: EGLenum = 0x3490; + +/// Possible color spaces for [`egl::GL_COLORSPACE`]. +/// +/// It is impossible to query whether a [`Config`] or [`Surface`] supports a +/// certain color space, only whether the [`Display`] might have it available +/// globally. Compare [`ColorSpace::egl_extension_name()`] against +/// [`GetDisplayExtensions::extensions()`] to get a hint of whether there will +/// be any support for it. +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] +pub enum ColorSpace { + /// Use [`egl::GL_COLORSPACE_LINEAR`] from [`EGL_KHR_gl_colorspace`](https://registry.khronos.org/EGL/extensions/KHR/EGL_KHR_gl_colorspace.txt). + Linear, + /// Use [`egl::GL_COLORSPACE_SRGB`] from [`EGL_KHR_gl_colorspace`](https://registry.khronos.org/EGL/extensions/KHR/EGL_KHR_gl_colorspace.txt). + Srgb, + /// Use [`egl::GL_COLORSPACE_SCRGB_EXT`] from [`EGL_EXT_gl_colorspace_scrgb`](https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_gl_colorspace_scrgb.txt). + Scrgb, + /// Use [`egl::GL_COLORSPACE_SCRGB_LINEAR_EXT`] from [`EGL_EXT_gl_colorspace_scrgb_linear`](https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_gl_colorspace_scrgb_linear.txt). + ScrgbLinear, + /// Use [`egl::GL_COLORSPACE_DISPLAY_P3_EXT`] from [`EGL_EXT_gl_colorspace_display_p3`](https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_gl_colorspace_display_p3.txt). + DisplayP3, + /// Use [`egl::GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT`] from [`EGL_EXT_gl_colorspace_display_p3_linear`](https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_gl_colorspace_display_p3.txt). + DisplayP3Linear, + /// Use [`EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT`] from [`EGL_EXT_gl_colorspace_display_p3_passthrough`](https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_gl_colorspace_display_p3_passthrough.txt). + DisplayP3Passthrough, + /// Use [`EGL_GL_COLORSPACE_BT2020_HLG_EXT`] from [`EGL_EXT_gl_colorspace_bt2020_hlg`](https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_gl_colorspace_bt2020_linear.txt). + Bt2020Hlg, + /// Use [`egl::GL_COLORSPACE_BT2020_LINEAR_EXT`] from [`EGL_EXT_gl_colorspace_bt2020_linear`](https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_gl_colorspace_bt2020_linear.txt). + Bt2020Linear, + /// Use [`egl::GL_COLORSPACE_BT2020_PQ_EXT`] from [`EGL_EXT_gl_colorspace_bt2020_pq`](https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_gl_colorspace_bt2020_linear.txt). + Bt2020Pq, +} + +impl ColorSpace { + fn egl_colorspace(self) -> EGLenum { + match self { + ColorSpace::Linear => egl::GL_COLORSPACE_LINEAR, + ColorSpace::Srgb => egl::GL_COLORSPACE_SRGB, + ColorSpace::Scrgb => egl::GL_COLORSPACE_SCRGB_EXT, + ColorSpace::ScrgbLinear => egl::GL_COLORSPACE_SCRGB_LINEAR_EXT, + ColorSpace::DisplayP3 => egl::GL_COLORSPACE_DISPLAY_P3_EXT, + ColorSpace::DisplayP3Linear => egl::GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT, + ColorSpace::DisplayP3Passthrough => EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT, + ColorSpace::Bt2020Hlg => EGL_GL_COLORSPACE_BT2020_HLG_EXT, + ColorSpace::Bt2020Linear => egl::GL_COLORSPACE_BT2020_LINEAR_EXT, + ColorSpace::Bt2020Pq => egl::GL_COLORSPACE_BT2020_PQ_EXT, + } + } + + fn from_egl_colorspace(attrib: EGLenum) -> Option { + Some(match attrib { + egl::GL_COLORSPACE_LINEAR => Self::Linear, + egl::GL_COLORSPACE_SRGB => Self::Srgb, + egl::GL_COLORSPACE_SCRGB_EXT => Self::Scrgb, + egl::GL_COLORSPACE_SCRGB_LINEAR_EXT => Self::ScrgbLinear, + egl::GL_COLORSPACE_DISPLAY_P3_EXT => Self::DisplayP3, + egl::GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT => Self::DisplayP3Linear, + EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT => Self::DisplayP3Passthrough, + EGL_GL_COLORSPACE_BT2020_HLG_EXT => Self::Bt2020Hlg, + egl::GL_COLORSPACE_BT2020_LINEAR_EXT => Self::Bt2020Linear, + egl::GL_COLORSPACE_BT2020_PQ_EXT => Self::Bt2020Pq, + _ => return None, + }) + } + + /// Returns the EGL extension name that provides this constant + pub const fn egl_extension_name(self) -> &'static str { + match self { + ColorSpace::Linear => "EGL_KHR_gl_colorspace", + ColorSpace::Srgb => "EGL_KHR_gl_colorspace", + ColorSpace::Scrgb => "EGL_EXT_gl_colorspace_scrgb", + ColorSpace::ScrgbLinear => "EGL_EXT_gl_colorspace_scrgb_linear", + ColorSpace::DisplayP3 => "EGL_EXT_gl_colorspace_display_p3", + ColorSpace::DisplayP3Linear => "EGL_EXT_gl_colorspace_display_p3_linear", + ColorSpace::DisplayP3Passthrough => "EGL_EXT_gl_colorspace_display_p3_passthrough", + ColorSpace::Bt2020Hlg => "EGL_EXT_gl_colorspace_bt2020_hlg", + ColorSpace::Bt2020Linear => "EGL_EXT_gl_colorspace_bt2020_linear", + ColorSpace::Bt2020Pq => "EGL_EXT_gl_colorspace_bt2020_pq", + } + } +} + +/// Attributes which are used for creating a particular surface in EGL. +// TODO: Do we need a builder here? +#[derive(Default, Debug, Clone)] +pub struct EglSurfaceAttributes { + /// Backend-agnostic [`Surface`] attributes + pub attributes: SurfaceAttributes, + /// If [`None`], no [`egl::GL_COLORSPACE`] is selected. + pub color_space: Option, +} + +impl Deref for EglSurfaceAttributes { + type Target = SurfaceAttributes; + + /// WARNING! This deref might also get used when passing + /// `EglSurfaceAttributes` into the generic `create_window_surface()`, + /// and you'll loose the `color_space` field! + fn deref(&self) -> &Self::Target { + &self.attributes + } +} + +impl From> for EglSurfaceAttributes { + fn from(attributes: SurfaceAttributes) -> Self { + Self { + color_space: attributes.srgb.map(|b| match b { + false => ColorSpace::Linear, + true => ColorSpace::Srgb, + }), + attributes, + } + } +} + impl Display { pub(crate) unsafe fn create_pbuffer_surface( &self, @@ -40,6 +162,8 @@ impl Display { // XXX Window surface is using `EGLAttrib` and not `EGLint`. let mut attrs = Vec::::with_capacity(ATTR_SIZE_HINT); + // TODO: Do pbuffers support HDR formats? + // Add dimensions. attrs.push(egl::WIDTH as EGLint); attrs.push(width.get() as EGLint); @@ -68,22 +192,31 @@ impl Display { }) } - pub(crate) unsafe fn create_pixmap_surface( + /// # Safety + /// Raw calls + pub unsafe fn create_pixmap_surface( &self, config: &Config, - surface_attributes: &SurfaceAttributes, + surface_attributes: &EglSurfaceAttributes, ) -> Result> { let native_pixmap = surface_attributes.native_pixmap.as_ref().unwrap(); let mut attrs = Vec::::with_capacity(ATTR_SIZE_HINT); - if surface_attributes.srgb.is_some() && config.srgb_capable() { + // Add colorspace if the extension is present. + if let Some(color_space) = surface_attributes.color_space { + if !self.inner.display_extensions.contains("EGL_KHR_gl_colorspace") { + return Err(ErrorKind::NotSupported( + "Setting a color space requires EGL_KHR_gl_colorspace", + ) + .into()); + } + if !self.inner.display_extensions.contains(color_space.egl_extension_name()) { + return Err(ErrorKind::NotSupported(color_space.egl_extension_name()).into()); + } + let color_attr = color_space.egl_colorspace(); attrs.push(egl::GL_COLORSPACE as EGLAttrib); - let colorspace = match surface_attributes.srgb { - Some(true) => egl::GL_COLORSPACE_SRGB as EGLAttrib, - _ => egl::GL_COLORSPACE_LINEAR as EGLAttrib, - }; - attrs.push(colorspace); + attrs.push(color_attr as EGLAttrib); } // Push `egl::NONE` to terminate the list. @@ -157,10 +290,12 @@ impl Display { }) } - pub(crate) unsafe fn create_window_surface( + /// # Safety + /// Raw calls + pub unsafe fn create_window_surface( &self, config: &Config, - surface_attributes: &SurfaceAttributes, + surface_attributes: &EglSurfaceAttributes, ) -> Result> { // Create native window. let native_window = NativeWindow::new( @@ -179,14 +314,17 @@ impl Display { as EGLAttrib; attrs.push(buffer); - // // Add colorspace if the extension is present. - if surface_attributes.srgb.is_some() && config.srgb_capable() { - attrs.push(egl::GL_COLORSPACE as EGLAttrib); - let colorspace = match surface_attributes.srgb { - Some(true) => egl::GL_COLORSPACE_SRGB as EGLAttrib, - _ => egl::GL_COLORSPACE_LINEAR as EGLAttrib, - }; - attrs.push(colorspace); + // Add colorspace if the extension is present. + if let Some(color_space) = surface_attributes.color_space { + if !self.inner.display_extensions.contains("EGL_KHR_gl_colorspace") { + return Err(ErrorKind::NotSupported( + "Setting a color space requires EGL_KHR_gl_colorspace", + ) + .into()); + } + if !self.inner.display_extensions.contains(color_space.egl_extension_name()) { + return Err(ErrorKind::NotSupported(color_space.egl_extension_name()).into()); + } } // Push `egl::NONE` to terminate the list. @@ -307,17 +445,26 @@ impl Surface { /// # Safety /// /// The caller must ensure that the attribute could be present. - unsafe fn raw_attribute(&self, attr: EGLint) -> EGLint { - unsafe { - let mut value = 0; - self.display.inner.egl.QuerySurface( - *self.display.inner.raw, - self.raw, - attr, - &mut value, - ); - value + pub unsafe fn raw_attribute(&self, attr: EGLint) -> EGLint { + let mut value = 0; + let success = unsafe { + self.display.inner.egl.QuerySurface(*self.display.inner.raw, self.raw, attr, &mut value) + }; + if success != 1 { + eprintln!("Could not read Attrib {attr:#0x} from {:?}", self) + } + value + } + + /// Returns the [`ColorSpace`] of the [`Surface`], or [`None`] if + /// `EGL_KHR_gl_colorspace` is not supported or the returned value is + /// not recognized by [`ColorSpace`]. + pub fn color_space(&self) -> Option { + if !self.display.inner.display_extensions.contains("EGL_KHR_gl_colorspace") { + return None; } + let color_space = unsafe { self.raw_attribute(egl::GL_COLORSPACE as EGLint) }; + ColorSpace::from_egl_colorspace(color_space as EGLenum) } } diff --git a/glutin/src/api/glx/config.rs b/glutin/src/api/glx/config.rs index 6950ce7b4b..4bfbc5be74 100644 --- a/glutin/src/api/glx/config.rs +++ b/glutin/src/api/glx/config.rs @@ -193,16 +193,19 @@ impl Config { /// /// The caller must ensure that the attribute could be present. unsafe fn raw_attribute(&self, attr: c_int) -> c_int { - unsafe { - let mut val = 0; + let mut val = 0; + let err = unsafe { self.inner.display.inner.glx.GetFBConfigAttrib( self.inner.display.inner.raw.cast(), *self.inner.raw, attr, &mut val, - ); - val as c_int + ) + }; + if err != 0 { + eprintln!("Could not read Attrib {attr:#0x} from {:?}", self) } + val as c_int } pub(crate) fn is_single_buffered(&self) -> bool { @@ -248,10 +251,13 @@ impl GlConfig for Config { } fn srgb_capable(&self) -> bool { - if self.inner.display.inner.client_extensions.contains("GLX_ARB_framebuffer_sRGB") { + // TODO(Marijn): Use DisplayFeatures::SRGB_FRAMEBUFFERS + let client_extensions = &self.inner.display.inner.client_extensions; + if client_extensions.contains("GLX_ARB_framebuffer_sRGB") + || client_extensions.contains("GLX_EXT_framebuffer_sRGB") + { + // Attribute is the same for EXT an ARB unsafe { self.raw_attribute(glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int) != 0 } - } else if self.inner.display.inner.client_extensions.contains("GLX_EXT_framebuffer_sRGB") { - unsafe { self.raw_attribute(glx_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int) != 0 } } else { false } diff --git a/glutin/src/api/glx/surface.rs b/glutin/src/api/glx/surface.rs index e39a0c1259..4b46d48577 100644 --- a/glutin/src/api/glx/surface.rs +++ b/glutin/src/api/glx/surface.rs @@ -167,18 +167,18 @@ impl Surface { /// /// The caller must ensure that the attribute could be present. unsafe fn raw_attribute(&self, attr: c_int) -> c_uint { + let mut value = 0; + // This shouldn't generate any errors given that we know that the surface is + // valid. unsafe { - let mut value = 0; - // This shouldn't generate any errors given that we know that the surface is - // valid. self.display.inner.glx.QueryDrawable( self.display.inner.raw.cast(), self.raw, attr, &mut value, - ); - value - } + ) + }; + value } } diff --git a/glutin/src/api/wgl/config.rs b/glutin/src/api/wgl/config.rs index 9c8448c89c..aece7d0e3b 100644 --- a/glutin/src/api/wgl/config.rs +++ b/glutin/src/api/wgl/config.rs @@ -315,10 +315,10 @@ impl Config { /// # Safety /// /// The caller must ensure that the attribute could be present. - unsafe fn raw_attribute(&self, attr: c_int) -> c_int { - unsafe { - let wgl_extra = self.inner.display.inner.wgl_extra.unwrap(); - let mut res = 0; + pub(super) unsafe fn raw_attribute(&self, attr: c_int) -> c_int { + let wgl_extra = self.inner.display.inner.wgl_extra.unwrap(); + let mut res = 0; + let success = unsafe { wgl_extra.GetPixelFormatAttribivARB( self.inner.hdc as *const _, self.inner.pixel_format_index, @@ -326,9 +326,12 @@ impl Config { 1, &attr, &mut res, - ); - res + ) + }; + if success != 1 { + eprintln!("Could not read Attrib {attr:#0x} from {:?}", self) } + res } } @@ -363,12 +366,14 @@ impl GlConfig for Config { } fn srgb_capable(&self) -> bool { - if self.inner.display.inner.client_extensions.contains(SRGB_EXT) - || self.inner.display.inner.client_extensions.contains("WGL_EXT_colorspace") + // TODO(Marijn): Use DisplayFeatures::SRGB_FRAMEBUFFERS + let client_extensions = &self.inner.display.inner.client_extensions; + if client_extensions.contains(SRGB_EXT) + || client_extensions.contains(SRGB_ARB) + || client_extensions.contains("WGL_EXT_colorspace") { + // Attribute is the same for EXT an ARB unsafe { self.raw_attribute(wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int) != 0 } - } else if self.inner.display.inner.client_extensions.contains(SRGB_ARB) { - unsafe { self.raw_attribute(wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int) != 0 } } else { false } diff --git a/glutin/src/config.rs b/glutin/src/config.rs index 8cb7626e0e..bb001bfb2d 100644 --- a/glutin/src/config.rs +++ b/glutin/src/config.rs @@ -45,9 +45,14 @@ pub trait GlConfig: Sealed { /// Zero would mean that there're no samples. fn num_samples(&self) -> u8; - /// Whether the config supports creating srgb capable [`Surface`]. + /// Whether the config supports creating an sRGB-capable [`Surface`]. /// /// [`Surface`]: crate::surface::Surface + /// + /// # Platform specific + /// - **EGL:** this only returns whether the `EGLDisplay` supports the sRGB + /// framebuffer extension globally, and will be the same value for every + /// [`GlConfig`]. fn srgb_capable(&self) -> bool; /// Whether the config supports creating transparent surfaces. @@ -95,10 +100,15 @@ pub struct ConfigTemplateBuilder { impl ConfigTemplateBuilder { /// Create a new configuration template builder. #[inline] + // TODO: Can we rename this to rgba8? pub fn new() -> Self { Default::default() } + // TODO: Can we add some common formats? + // pub fn r10g10b10a2() -> Self {} + // pub fn r16g16b16a16() -> Self {} + /// Number of alpha bits in the color buffer. /// /// By default `8` is requested. diff --git a/glutin/src/context.rs b/glutin/src/context.rs index 7d14582fef..f50e898606 100644 --- a/glutin/src/context.rs +++ b/glutin/src/context.rs @@ -67,7 +67,7 @@ pub trait NotCurrentGlContext: Sealed { /// The same as [`Self::make_current`], but provides a way to set read and /// draw surfaces. /// - /// # Api-specific: + /// # Api-specific /// /// - **WGL/CGL:** not supported. fn make_current_draw_read( @@ -107,7 +107,7 @@ pub trait PossiblyCurrentGlContext: Sealed { /// The same as [`Self::make_current`] but provides a way to set read and /// draw surfaces explicitly. /// - /// # Api-specific: + /// # Api-specific /// /// - **CGL/WGL:** not supported. fn make_current_draw_read( diff --git a/glutin/src/display.rs b/glutin/src/display.rs index 25aa99888b..c0df0eb8f1 100644 --- a/glutin/src/display.rs +++ b/glutin/src/display.rs @@ -322,7 +322,9 @@ impl GlDisplay for Display { match (self, config) { #[cfg(egl_backend)] (Self::Egl(display), Config::Egl(config)) => unsafe { - Ok(Surface::Egl(display.create_window_surface(config, surface_attributes)?)) + Ok(Surface::Egl( + display.create_window_surface(config, &surface_attributes.clone().into())?, + )) }, #[cfg(glx_backend)] (Self::Glx(display), Config::Glx(config)) => unsafe { @@ -374,7 +376,9 @@ impl GlDisplay for Display { match (self, config) { #[cfg(egl_backend)] (Self::Egl(display), Config::Egl(config)) => unsafe { - Ok(Surface::Egl(display.create_pixmap_surface(config, surface_attributes)?)) + Ok(Surface::Egl( + display.create_pixmap_surface(config, &surface_attributes.clone().into())?, + )) }, #[cfg(glx_backend)] (Self::Glx(display), Config::Glx(config)) => unsafe { @@ -429,8 +433,8 @@ pub enum DisplayApiPreference { /// /// # Platform-specific /// - /// **Windows:** ANGLE can be used if `libEGL.dll` and `libGLESv2.dll` are - /// in the library search path. + /// - **Windows:** ANGLE can be used if `libEGL.dll` and `libGLESv2.dll` are + /// in the library search path. #[cfg(egl_backend)] Egl, @@ -552,7 +556,7 @@ bitflags! { /// The display supports creating context with explicit [`release behavior`]. /// /// [`release behavior`]: crate::context::ReleaseBehavior - const CONTEXT_RELEASE_BEHAVIOR = 0b0001_0000; + const CONTEXT_RELEASE_BEHAVIOR = 0b0001_0000; /// The display supports creating OpenGL ES [`context`]. /// diff --git a/glutin/src/surface.rs b/glutin/src/surface.rs index e7f6aa2b93..48b5a420cf 100644 --- a/glutin/src/surface.rs +++ b/glutin/src/surface.rs @@ -119,13 +119,22 @@ impl SurfaceAttributesBuilder { Default::default() } - /// Specify whether the surface should support srgb or not. Passing `None` + /// Specify whether the surface should support srgb or not. Passing [`None`] /// means you don't care. /// - /// # Api-specific. + /// # Api-specific /// /// This only controls EGL surfaces, other platforms use the context for - /// that. + /// that. More color spaces besides sRGB are available on the + #[cfg_attr( + egl_backend, + doc = "[`EglSurfaceAttributes::color_space`][crate::api::egl::surface::EglSurfaceAttributes::color_space]" + )] + #[cfg_attr( + not(egl_backend), + doc = "[`EglSurfaceAttributes::color_space`](https://docs.rs/glutin/latest/glutin/api/egl/surface/struct.EglSurfaceAttributes.html#structfield.color_space)" + )] + /// field. pub fn with_srgb(mut self, srgb: Option) -> Self { self.attributes.srgb = srgb; self @@ -140,7 +149,7 @@ impl SurfaceAttributesBuilder { /// /// The surface is requested as double buffered by default. /// - /// # Api-specific. + /// # Api-specific /// /// This is EGL specific, other platforms use the context for that. pub fn with_single_buffer(mut self, single_buffer: bool) -> Self { diff --git a/glutin_egl_sys/build.rs b/glutin_egl_sys/build.rs index c8dbedf87a..226fbb8d52 100644 --- a/glutin_egl_sys/build.rs +++ b/glutin_egl_sys/build.rs @@ -19,35 +19,50 @@ fn main() { || target.contains("ios") { let mut file = File::create(dest.join("egl_bindings.rs")).unwrap(); - let reg = Registry::new(Api::Egl, (1, 5), Profile::Core, Fallbacks::All, [ - "EGL_ANDROID_native_fence_sync", - "EGL_EXT_buffer_age", - "EGL_EXT_create_context_robustness", - "EGL_EXT_device_base", - "EGL_EXT_device_drm", - "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", - "EGL_EXT_platform_wayland", - "EGL_EXT_platform_x11", - "EGL_EXT_swap_buffers_with_damage", - "EGL_KHR_create_context", - "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", - "EGL_KHR_platform_x11", - "EGL_KHR_swap_buffers_with_damage", - "EGL_KHR_wait_sync", - "EGL_MESA_platform_gbm", - ]); + let reg = Registry::new( + Api::Egl, + (1, 5), + Profile::Core, + Fallbacks::All, + [ + "EGL_ANDROID_native_fence_sync", + "EGL_EXT_buffer_age", + "EGL_EXT_create_context_robustness", + "EGL_EXT_device_base", + "EGL_EXT_device_drm", + "EGL_EXT_device_drm_render_node", + "EGL_EXT_device_enumeration", + "EGL_EXT_device_query", + "EGL_EXT_device_query_name", + "EGL_EXT_gl_colorspace_bt2020_hlg", + "EGL_EXT_gl_colorspace_bt2020_linear", + "EGL_EXT_gl_colorspace_bt2020_pq", + "EGL_EXT_gl_colorspace_display_p3", + "EGL_EXT_gl_colorspace_display_p3_linear", + "EGL_EXT_gl_colorspace_display_p3_passthrough", + "EGL_EXT_gl_colorspace_scrgb", + "EGL_EXT_gl_colorspace_scrgb_linear", + "EGL_EXT_pixel_format_float", + "EGL_EXT_platform_base", + "EGL_EXT_platform_device", + "EGL_EXT_platform_wayland", + "EGL_EXT_platform_x11", + "EGL_EXT_swap_buffers_with_damage", + "EGL_KHR_create_context", + "EGL_KHR_create_context_no_error", + "EGL_KHR_display_reference", + "EGL_KHR_fence_sync", + "EGL_KHR_image_base", + "EGL_KHR_gl_colorspace", + "EGL_KHR_platform_android", + "EGL_KHR_platform_gbm", + "EGL_KHR_platform_wayland", + "EGL_KHR_platform_x11", + "EGL_KHR_swap_buffers_with_damage", + "EGL_KHR_wait_sync", + "EGL_MESA_platform_gbm", + ], + ); if target.contains("ios") { reg.write_bindings(gl_generator::StaticStructGenerator, &mut file) diff --git a/glutin_examples/src/lib.rs b/glutin_examples/src/lib.rs index e997dd01d3..92ca5a9e66 100644 --- a/glutin_examples/src/lib.rs +++ b/glutin_examples/src/lib.rs @@ -10,11 +10,11 @@ use winit::event::{KeyEvent, WindowEvent}; use winit::keyboard::{Key, NamedKey}; use winit::window::Window; -use glutin::config::{Config, ConfigTemplateBuilder}; +use glutin::config::{ColorBufferType, Config, ConfigTemplateBuilder}; use glutin::context::{ ContextApi, ContextAttributesBuilder, NotCurrentContext, PossiblyCurrentContext, Version, }; -use glutin::display::GetGlDisplay; +use glutin::display::GetGlDisplay as _; use glutin::prelude::*; use glutin::surface::{Surface, SwapInterval, WindowSurface}; @@ -40,8 +40,13 @@ pub fn main(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), Box { - // Some platforms like EGL require resizing GL surface to update the size + // Some platforms like EGL require resizing GL surface to update the size. // Notable platforms here are Wayland and macOS, other don't require it // and the function is no-op, but it's wise to resize it for portability // reasons. @@ -233,10 +244,22 @@ struct AppState { pub fn gl_config_picker(configs: Box + '_>) -> Config { configs .reduce(|accum, config| { + let mut higher_bit_depth = false; + if let Some(ColorBufferType::Rgb { r_size, g_size, b_size }) = accum.color_buffer_type() + { + if let Some(ColorBufferType::Rgb { r_size: r, g_size: g, b_size: b }) = + config.color_buffer_type() + { + higher_bit_depth = r > r_size || g > g_size || b > b_size; + } + } let transparency_check = config.supports_transparency().unwrap_or(false) & !accum.supports_transparency().unwrap_or(false); - if transparency_check || config.num_samples() > accum.num_samples() { + // TODO: Note that there's no preference order here. We accept the new config + // based on any of these changes + if transparency_check || config.num_samples() > accum.num_samples() || higher_bit_depth + { config } else { accum @@ -375,12 +398,26 @@ impl Drop for Renderer { unsafe fn create_shader( gl: &gl::Gl, - shader: gl::types::GLenum, + shader_type: gl::types::GLenum, source: &[u8], ) -> gl::types::GLuint { - let shader = gl.CreateShader(shader); + let shader = gl.CreateShader(shader_type); gl.ShaderSource(shader, 1, [source.as_ptr().cast()].as_ptr(), std::ptr::null()); gl.CompileShader(shader); + let mut len = 0; + gl.GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len); + if len > 0 { + let mut log = Vec::::with_capacity(len as usize); + gl.GetShaderInfoLog(shader, len, &mut len, log.as_mut_ptr().cast()); + log.set_len(len as usize); + log.push(0); + let log = CString::from_vec_with_nul(log).unwrap(); + eprintln!("Shader {shader_type:?} log"); + eprintln!("{}", log.to_string_lossy()); + } + let mut status = 0; + gl.GetShaderiv(shader, gl::COMPILE_STATUS, &mut status); + assert_eq!(status, 1, "Shader {shader_type:?} compilation failed"); shader } diff --git a/glutin_glx_sys/build.rs b/glutin_glx_sys/build.rs index c9ce54b70b..0c933efb1a 100644 --- a/glutin_glx_sys/build.rs +++ b/glutin_glx_sys/build.rs @@ -21,22 +21,28 @@ fn main() { .unwrap(); let mut file = File::create(dest.join("glx_extra_bindings.rs")).unwrap(); - Registry::new(Api::Glx, (1, 4), Profile::Core, Fallbacks::All, [ - "GLX_ARB_context_flush_control", - "GLX_ARB_create_context", - "GLX_ARB_create_context_no_error", - "GLX_ARB_create_context_profile", - "GLX_ARB_create_context_robustness", - "GLX_ARB_fbconfig_float", - "GLX_ARB_framebuffer_sRGB", - "GLX_ARB_multisample", - "GLX_EXT_buffer_age", - "GLX_EXT_create_context_es2_profile", - "GLX_EXT_framebuffer_sRGB", - "GLX_EXT_swap_control", - "GLX_MESA_swap_control", - "GLX_SGI_swap_control", - ]) + Registry::new( + Api::Glx, + (1, 4), + Profile::Core, + Fallbacks::All, + [ + "GLX_ARB_context_flush_control", + "GLX_ARB_create_context", + "GLX_ARB_create_context_no_error", + "GLX_ARB_create_context_profile", + "GLX_ARB_create_context_robustness", + "GLX_ARB_fbconfig_float", + "GLX_ARB_framebuffer_sRGB", + "GLX_ARB_multisample", + "GLX_EXT_buffer_age", + "GLX_EXT_create_context_es2_profile", + "GLX_EXT_framebuffer_sRGB", + "GLX_EXT_swap_control", + "GLX_MESA_swap_control", + "GLX_SGI_swap_control", + ], + ) .write_bindings(gl_generator::StructGenerator, &mut file) .unwrap(); } diff --git a/glutin_wgl_sys/build.rs b/glutin_wgl_sys/build.rs index ab15682fd0..461395a363 100644 --- a/glutin_wgl_sys/build.rs +++ b/glutin_wgl_sys/build.rs @@ -16,22 +16,28 @@ fn main() { .unwrap(); let mut file = File::create(dest.join("wgl_extra_bindings.rs")).unwrap(); - Registry::new(Api::Wgl, (1, 0), Profile::Core, Fallbacks::All, [ - "WGL_ARB_context_flush_control", - "WGL_ARB_create_context", - "WGL_ARB_create_context_no_error", - "WGL_ARB_create_context_profile", - "WGL_ARB_create_context_robustness", - "WGL_ARB_extensions_string", - "WGL_ARB_framebuffer_sRGB", - "WGL_ARB_multisample", - "WGL_ARB_pixel_format", - "WGL_ARB_pixel_format_float", - "WGL_EXT_create_context_es2_profile", - "WGL_EXT_extensions_string", - "WGL_EXT_framebuffer_sRGB", - "WGL_EXT_swap_control", - ]) + Registry::new( + Api::Wgl, + (1, 0), + Profile::Core, + Fallbacks::All, + [ + "WGL_ARB_context_flush_control", + "WGL_ARB_create_context", + "WGL_ARB_create_context_no_error", + "WGL_ARB_create_context_profile", + "WGL_ARB_create_context_robustness", + "WGL_ARB_extensions_string", + "WGL_ARB_framebuffer_sRGB", + "WGL_ARB_multisample", + "WGL_ARB_pixel_format", + "WGL_ARB_pixel_format_float", + "WGL_EXT_create_context_es2_profile", + "WGL_EXT_extensions_string", + "WGL_EXT_framebuffer_sRGB", + "WGL_EXT_swap_control", + ], + ) .write_bindings(gl_generator::StructGenerator, &mut file) .unwrap(); }