From fae8c1390090cec25980f696965c7a5de73ac5d4 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 | 14 +-- glutin/src/api/wgl/config.rs | 25 ++-- glutin/src/api/wgl/surface.rs | 2 +- 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 | 9 ++ glutin_examples/src/lib.rs | 118 ++++++++++++++++--- 15 files changed, 386 insertions(+), 93 deletions(-) diff --git a/glutin-winit/src/lib.rs b/glutin-winit/src/lib.rs index d0af719047..ea8f110b30 100644 --- a/glutin-winit/src/lib.rs +++ b/glutin-winit/src/lib.rs @@ -83,9 +83,9 @@ impl DisplayBuilder { /// /// # Api-specific /// - /// **WGL:** - [`WindowBuilder`] **must** be passed in - /// [`Self::with_window_builder`] if modern OpenGL(ES) is desired, - /// otherwise only builtin functions like `glClear` will be available. + /// - **WGL:** [`WindowBuilder`] **must** be passed in + /// [`Self::with_window_builder`] if modern OpenGL(ES) is desired, + /// otherwise only builtin functions like `glClear` will be available. pub fn build( mut self, window_target: &EventLoopWindowTarget, diff --git a/glutin/src/api/cgl/config.rs b/glutin/src/api/cgl/config.rs index f1377039fa..84c6fb0705 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 e400736cc8..99f7396617 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 ea64ba66b2..81ccd614ca 100644 --- a/glutin/src/api/egl/display.rs +++ b/glutin/src/api/egl/display.rs @@ -26,6 +26,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}; @@ -511,12 +513,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( @@ -535,12 +540,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 102c74077e..e37cfc40cd 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. @@ -304,17 +442,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 aeaa42386d..3504ef462b 100644 --- a/glutin/src/api/glx/surface.rs +++ b/glutin/src/api/glx/surface.rs @@ -164,18 +164,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 } } @@ -235,7 +235,7 @@ impl GlSurface for Surface { }, _ => { return Err( - ErrorKind::NotSupported("swap contol extrensions are not supported").into() + ErrorKind::NotSupported("swap control extensions are not supported").into() ); }, }; diff --git a/glutin/src/api/wgl/config.rs b/glutin/src/api/wgl/config.rs index ad5fabbe8d..f2e37917b2 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/api/wgl/surface.rs b/glutin/src/api/wgl/surface.rs index fb3c204666..beeef32f5b 100644 --- a/glutin/src/api/wgl/surface.rs +++ b/glutin/src/api/wgl/surface.rs @@ -141,7 +141,7 @@ impl GlSurface for Surface { }, _ => { return Err( - ErrorKind::NotSupported("swap contol extrensions are not supported").into() + ErrorKind::NotSupported("swap control extensions are not supported").into() ) }, }; 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 9e5dd954a9..fab51a57f6 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 cdde086a04..533b6eaa44 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 337458bd61..98ce657e34 100644 --- a/glutin_egl_sys/build.rs +++ b/glutin_egl_sys/build.rs @@ -29,6 +29,14 @@ fn main() { "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", @@ -39,6 +47,7 @@ fn main() { "EGL_KHR_create_context_no_error", "EGL_KHR_display_reference", "EGL_KHR_fence_sync", + "EGL_KHR_gl_colorspace", "EGL_KHR_platform_android", "EGL_KHR_platform_gbm", "EGL_KHR_platform_wayland", diff --git a/glutin_examples/src/lib.rs b/glutin_examples/src/lib.rs index 393d1beb7d..810464c479 100644 --- a/glutin_examples/src/lib.rs +++ b/glutin_examples/src/lib.rs @@ -8,11 +8,11 @@ use winit::event::{Event, KeyEvent, WindowEvent}; use winit::keyboard::{Key, NamedKey}; use winit::window::WindowBuilder; -use glutin::config::ConfigTemplateBuilder; +use glutin::config::{ColorBufferType, ConfigTemplateBuilder}; use glutin::context::{ContextApi, ContextAttributesBuilder, Version}; -use glutin::display::GetGlDisplay; +use glutin::display::GetGlDisplay as _; use glutin::prelude::*; -use glutin::surface::SwapInterval; +use glutin::surface::{SurfaceAttributesBuilder, SwapInterval}; use glutin_winit::{self, DisplayBuilder, GlWindow}; @@ -43,8 +43,13 @@ pub fn main(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), Box) -> Result<(), Box 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 @@ -65,7 +85,13 @@ pub fn main(event_loop: winit::event_loop::EventLoop<()>) -> Result<(), Box) -> Result<(), Box { + eprintln!("Color space {color_space:?} not supported: {e:?}"); + None + }, + Ok(s) => Some(s), + } + }) + .expect("Could not create surface"); + println!("Picked surface with color space {:?}", s.color_space()); + glutin::surface::Surface::Egl(s) + } else { + unsafe { gl_display.create_window_surface(&gl_config, &attrs) }.unwrap() }; + #[cfg(not(egl_backend))] + let gl_surface = + unsafe { gl_display.create_window_surface(&gl_config, &attrs) }.unwrap(); + // Make it current. let gl_context = not_current_gl_context.take().unwrap().make_current(&gl_surface).unwrap(); @@ -281,7 +359,7 @@ impl Renderer { self.gl.BindVertexArray(self.vao); self.gl.BindBuffer(gl::ARRAY_BUFFER, self.vbo); - self.gl.ClearColor(0.1, 0.1, 0.1, 0.9); + self.gl.ClearColor(0.1, 0.1, 0.1, 0.7); self.gl.Clear(gl::COLOR_BUFFER_BIT); self.gl.DrawArrays(gl::TRIANGLES, 0, 3); } @@ -314,12 +392,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 }