Skip to content

Commit 26dab10

Browse files
committed
EGL: Provide color-space support
1 parent ee00a4f commit 26dab10

File tree

15 files changed

+386
-93
lines changed

15 files changed

+386
-93
lines changed

glutin-winit/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,9 @@ impl DisplayBuilder {
8383
///
8484
/// # Api-specific
8585
///
86-
/// **WGL:** - [`WindowBuilder`] **must** be passed in
87-
/// [`Self::with_window_builder`] if modern OpenGL(ES) is desired,
88-
/// otherwise only builtin functions like `glClear` will be available.
86+
/// - **WGL:** [`WindowBuilder`] **must** be passed in
87+
/// [`Self::with_window_builder`] if modern OpenGL(ES) is desired,
88+
/// otherwise only builtin functions like `glClear` will be available.
8989
pub fn build<T, Picker>(
9090
mut self,
9191
window_target: &EventLoopWindowTarget<T>,

glutin/src/api/cgl/config.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -136,16 +136,16 @@ pub struct Config {
136136

137137
impl Config {
138138
fn raw_attribute(&self, attrib: NSOpenGLPixelFormatAttribute) -> i32 {
139+
let mut value = 0;
139140
unsafe {
140-
let mut value = 0;
141141
self.inner.raw.getValues_forAttribute_forVirtualScreen(
142142
&mut value, attrib,
143143
// They do differ per monitor and require context. Which is kind of insane, but
144144
// whatever. Zero is a primary monitor.
145145
0,
146-
);
147-
value
148-
}
146+
)
147+
};
148+
value
149149
}
150150

151151
#[allow(deprecated)]

glutin/src/api/egl/config.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -241,16 +241,19 @@ impl Config {
241241
///
242242
/// The caller must ensure that the attribute could be present.
243243
unsafe fn raw_attribute(&self, attr: EGLint) -> EGLint {
244-
unsafe {
245-
let mut val = 0;
244+
let mut val = 0;
245+
let success = unsafe {
246246
self.inner.display.inner.egl.GetConfigAttrib(
247247
*self.inner.display.inner.raw,
248248
*self.inner.raw,
249249
attr,
250250
&mut val,
251-
);
252-
val as EGLint
251+
)
252+
};
253+
if success != 1 {
254+
eprintln!("Could not read Attrib {attr:#0x} from {:?}", self)
253255
}
256+
val as EGLint
254257
}
255258
}
256259

glutin/src/api/egl/display.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ use crate::surface::{PbufferSurface, PixmapSurface, SurfaceAttributes, WindowSur
2626
use super::config::Config;
2727
use super::context::NotCurrentContext;
2828
use super::device::Device;
29+
#[cfg(doc)]
30+
use super::surface::ColorSpace;
2931
use super::surface::Surface;
3032

3133
use super::{Egl, EGL};
@@ -511,12 +513,15 @@ impl GlDisplay for Display {
511513
unsafe { Self::find_configs(self, template) }
512514
}
513515

516+
/// Creates a window surface without [`ColorSpace`]. Use
517+
/// [`Self::create_window_surface()`] if you wish to specify a color
518+
/// space.
514519
unsafe fn create_window_surface(
515520
&self,
516521
config: &Self::Config,
517522
surface_attributes: &SurfaceAttributes<WindowSurface>,
518523
) -> Result<Self::WindowSurface> {
519-
unsafe { Self::create_window_surface(self, config, surface_attributes) }
524+
unsafe { Self::create_window_surface(self, config, &surface_attributes.clone().into()) }
520525
}
521526

522527
unsafe fn create_pbuffer_surface(
@@ -535,12 +540,15 @@ impl GlDisplay for Display {
535540
unsafe { Self::create_context(self, config, context_attributes) }
536541
}
537542

543+
/// Creates a pixmap surface without [`ColorSpace`]. Use
544+
/// [`Self::create_pixmap_surface()`] if you wish to specify a color
545+
/// space.
538546
unsafe fn create_pixmap_surface(
539547
&self,
540548
config: &Self::Config,
541549
surface_attributes: &SurfaceAttributes<PixmapSurface>,
542550
) -> Result<Self::PixmapSurface> {
543-
unsafe { Self::create_pixmap_surface(self, config, surface_attributes) }
551+
unsafe { Self::create_pixmap_surface(self, config, &surface_attributes.clone().into()) }
544552
}
545553

546554
fn get_proc_address(&self, addr: &CStr) -> *const ffi::c_void {

glutin/src/api/egl/surface.rs

+176-30
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@
22
33
use std::marker::PhantomData;
44
use std::num::NonZeroU32;
5+
use std::ops::Deref;
56
use std::{ffi, fmt};
67

7-
use glutin_egl_sys::egl;
8-
use glutin_egl_sys::egl::types::{EGLAttrib, EGLSurface, EGLint};
8+
use glutin_egl_sys::egl::types::{EGLAttrib, EGLSurface};
9+
use glutin_egl_sys::egl::{self};
10+
use glutin_egl_sys::{EGLenum, EGLint};
911
use raw_window_handle::RawWindowHandle;
1012
#[cfg(wayland_platform)]
1113
use wayland_sys::{egl::*, ffi_dispatch};
1214

1315
use crate::api::egl::display::EglDisplay;
1416
use crate::config::GetGlConfig;
17+
#[cfg(doc)]
18+
use crate::display::GetDisplayExtensions;
1519
use crate::display::GetGlDisplay;
1620
use crate::error::{ErrorKind, Result};
1721
use crate::prelude::*;
@@ -28,6 +32,124 @@ use super::display::Display;
2832
/// Hint for the attribute list size.
2933
const ATTR_SIZE_HINT: usize = 8;
3034

35+
/// Missing `EGL_EXT_gl_colorspace_bt2020_hlg` constant defined at <https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_gl_colorspace_bt2020_linear.txt>
36+
pub const EGL_GL_COLORSPACE_BT2020_HLG_EXT: EGLenum = 0x3540;
37+
/// Missing `EXT_gl_colorspace_display_p3_passthrough` constant defined at <https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_gl_colorspace_display_p3_passthrough.txt>
38+
pub const EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT: EGLenum = 0x3490;
39+
40+
/// Possible color spaces for [`egl::GL_COLORSPACE`].
41+
///
42+
/// It is impossible to query whether a [`Config`] or [`Surface`] supports a
43+
/// certain color space, only whether the [`Display`] might have it available
44+
/// globally. Compare [`ColorSpace::egl_extension_name()`] against
45+
/// [`GetDisplayExtensions::extensions()`] to get a hint of whether there will
46+
/// be any support for it.
47+
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
48+
pub enum ColorSpace {
49+
/// Use [`egl::GL_COLORSPACE_LINEAR`] from [`EGL_KHR_gl_colorspace`](https://registry.khronos.org/EGL/extensions/KHR/EGL_KHR_gl_colorspace.txt).
50+
Linear,
51+
/// Use [`egl::GL_COLORSPACE_SRGB`] from [`EGL_KHR_gl_colorspace`](https://registry.khronos.org/EGL/extensions/KHR/EGL_KHR_gl_colorspace.txt).
52+
Srgb,
53+
/// 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).
54+
Scrgb,
55+
/// 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).
56+
ScrgbLinear,
57+
/// 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).
58+
DisplayP3,
59+
/// 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).
60+
DisplayP3Linear,
61+
/// 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).
62+
DisplayP3Passthrough,
63+
/// 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).
64+
Bt2020Hlg,
65+
/// 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).
66+
Bt2020Linear,
67+
/// 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).
68+
Bt2020Pq,
69+
}
70+
71+
impl ColorSpace {
72+
fn egl_colorspace(self) -> EGLenum {
73+
match self {
74+
ColorSpace::Linear => egl::GL_COLORSPACE_LINEAR,
75+
ColorSpace::Srgb => egl::GL_COLORSPACE_SRGB,
76+
ColorSpace::Scrgb => egl::GL_COLORSPACE_SCRGB_EXT,
77+
ColorSpace::ScrgbLinear => egl::GL_COLORSPACE_SCRGB_LINEAR_EXT,
78+
ColorSpace::DisplayP3 => egl::GL_COLORSPACE_DISPLAY_P3_EXT,
79+
ColorSpace::DisplayP3Linear => egl::GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT,
80+
ColorSpace::DisplayP3Passthrough => EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT,
81+
ColorSpace::Bt2020Hlg => EGL_GL_COLORSPACE_BT2020_HLG_EXT,
82+
ColorSpace::Bt2020Linear => egl::GL_COLORSPACE_BT2020_LINEAR_EXT,
83+
ColorSpace::Bt2020Pq => egl::GL_COLORSPACE_BT2020_PQ_EXT,
84+
}
85+
}
86+
87+
fn from_egl_colorspace(attrib: EGLenum) -> Option<Self> {
88+
Some(match attrib {
89+
egl::GL_COLORSPACE_LINEAR => Self::Linear,
90+
egl::GL_COLORSPACE_SRGB => Self::Srgb,
91+
egl::GL_COLORSPACE_SCRGB_EXT => Self::Scrgb,
92+
egl::GL_COLORSPACE_SCRGB_LINEAR_EXT => Self::ScrgbLinear,
93+
egl::GL_COLORSPACE_DISPLAY_P3_EXT => Self::DisplayP3,
94+
egl::GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT => Self::DisplayP3Linear,
95+
EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT => Self::DisplayP3Passthrough,
96+
EGL_GL_COLORSPACE_BT2020_HLG_EXT => Self::Bt2020Hlg,
97+
egl::GL_COLORSPACE_BT2020_LINEAR_EXT => Self::Bt2020Linear,
98+
egl::GL_COLORSPACE_BT2020_PQ_EXT => Self::Bt2020Pq,
99+
_ => return None,
100+
})
101+
}
102+
103+
/// Returns the EGL extension name that provides this constant
104+
pub const fn egl_extension_name(self) -> &'static str {
105+
match self {
106+
ColorSpace::Linear => "EGL_KHR_gl_colorspace",
107+
ColorSpace::Srgb => "EGL_KHR_gl_colorspace",
108+
ColorSpace::Scrgb => "EGL_EXT_gl_colorspace_scrgb",
109+
ColorSpace::ScrgbLinear => "EGL_EXT_gl_colorspace_scrgb_linear",
110+
ColorSpace::DisplayP3 => "EGL_EXT_gl_colorspace_display_p3",
111+
ColorSpace::DisplayP3Linear => "EGL_EXT_gl_colorspace_display_p3_linear",
112+
ColorSpace::DisplayP3Passthrough => "EGL_EXT_gl_colorspace_display_p3_passthrough",
113+
ColorSpace::Bt2020Hlg => "EGL_EXT_gl_colorspace_bt2020_hlg",
114+
ColorSpace::Bt2020Linear => "EGL_EXT_gl_colorspace_bt2020_linear",
115+
ColorSpace::Bt2020Pq => "EGL_EXT_gl_colorspace_bt2020_pq",
116+
}
117+
}
118+
}
119+
120+
/// Attributes which are used for creating a particular surface in EGL.
121+
// TODO: Do we need a builder here?
122+
#[derive(Default, Debug, Clone)]
123+
pub struct EglSurfaceAttributes<T: SurfaceTypeTrait> {
124+
/// Backend-agnostic [`Surface`] attributes
125+
pub attributes: SurfaceAttributes<T>,
126+
/// If [`None`], no [`egl::GL_COLORSPACE`] is selected.
127+
pub color_space: Option<ColorSpace>,
128+
}
129+
130+
impl<T: SurfaceTypeTrait> Deref for EglSurfaceAttributes<T> {
131+
type Target = SurfaceAttributes<T>;
132+
133+
/// WARNING! This deref might also get used when passing
134+
/// `EglSurfaceAttributes` into the generic `create_window_surface()`,
135+
/// and you'll loose the `color_space` field!
136+
fn deref(&self) -> &Self::Target {
137+
&self.attributes
138+
}
139+
}
140+
141+
impl<T: SurfaceTypeTrait> From<SurfaceAttributes<T>> for EglSurfaceAttributes<T> {
142+
fn from(attributes: SurfaceAttributes<T>) -> Self {
143+
Self {
144+
color_space: attributes.srgb.map(|b| match b {
145+
false => ColorSpace::Linear,
146+
true => ColorSpace::Srgb,
147+
}),
148+
attributes,
149+
}
150+
}
151+
}
152+
31153
impl Display {
32154
pub(crate) unsafe fn create_pbuffer_surface(
33155
&self,
@@ -40,6 +162,8 @@ impl Display {
40162
// XXX Window surface is using `EGLAttrib` and not `EGLint`.
41163
let mut attrs = Vec::<EGLint>::with_capacity(ATTR_SIZE_HINT);
42164

165+
// TODO: Do pbuffers support HDR formats?
166+
43167
// Add dimensions.
44168
attrs.push(egl::WIDTH as EGLint);
45169
attrs.push(width.get() as EGLint);
@@ -68,22 +192,31 @@ impl Display {
68192
})
69193
}
70194

71-
pub(crate) unsafe fn create_pixmap_surface(
195+
/// # Safety
196+
/// Raw calls
197+
pub unsafe fn create_pixmap_surface(
72198
&self,
73199
config: &Config,
74-
surface_attributes: &SurfaceAttributes<PixmapSurface>,
200+
surface_attributes: &EglSurfaceAttributes<PixmapSurface>,
75201
) -> Result<Surface<PixmapSurface>> {
76202
let native_pixmap = surface_attributes.native_pixmap.as_ref().unwrap();
77203

78204
let mut attrs = Vec::<EGLAttrib>::with_capacity(ATTR_SIZE_HINT);
79205

80-
if surface_attributes.srgb.is_some() && config.srgb_capable() {
206+
// Add colorspace if the extension is present.
207+
if let Some(color_space) = surface_attributes.color_space {
208+
if !self.inner.display_extensions.contains("EGL_KHR_gl_colorspace") {
209+
return Err(ErrorKind::NotSupported(
210+
"Setting a color space requires EGL_KHR_gl_colorspace",
211+
)
212+
.into());
213+
}
214+
if !self.inner.display_extensions.contains(color_space.egl_extension_name()) {
215+
return Err(ErrorKind::NotSupported(color_space.egl_extension_name()).into());
216+
}
217+
let color_attr = color_space.egl_colorspace();
81218
attrs.push(egl::GL_COLORSPACE as EGLAttrib);
82-
let colorspace = match surface_attributes.srgb {
83-
Some(true) => egl::GL_COLORSPACE_SRGB as EGLAttrib,
84-
_ => egl::GL_COLORSPACE_LINEAR as EGLAttrib,
85-
};
86-
attrs.push(colorspace);
219+
attrs.push(color_attr as EGLAttrib);
87220
}
88221

89222
// Push `egl::NONE` to terminate the list.
@@ -157,10 +290,12 @@ impl Display {
157290
})
158291
}
159292

160-
pub(crate) unsafe fn create_window_surface(
293+
/// # Safety
294+
/// Raw calls
295+
pub unsafe fn create_window_surface(
161296
&self,
162297
config: &Config,
163-
surface_attributes: &SurfaceAttributes<WindowSurface>,
298+
surface_attributes: &EglSurfaceAttributes<WindowSurface>,
164299
) -> Result<Surface<WindowSurface>> {
165300
// Create native window.
166301
let native_window = NativeWindow::new(
@@ -179,14 +314,17 @@ impl Display {
179314
as EGLAttrib;
180315
attrs.push(buffer);
181316

182-
// // Add colorspace if the extension is present.
183-
if surface_attributes.srgb.is_some() && config.srgb_capable() {
184-
attrs.push(egl::GL_COLORSPACE as EGLAttrib);
185-
let colorspace = match surface_attributes.srgb {
186-
Some(true) => egl::GL_COLORSPACE_SRGB as EGLAttrib,
187-
_ => egl::GL_COLORSPACE_LINEAR as EGLAttrib,
188-
};
189-
attrs.push(colorspace);
317+
// Add colorspace if the extension is present.
318+
if let Some(color_space) = surface_attributes.color_space {
319+
if !self.inner.display_extensions.contains("EGL_KHR_gl_colorspace") {
320+
return Err(ErrorKind::NotSupported(
321+
"Setting a color space requires EGL_KHR_gl_colorspace",
322+
)
323+
.into());
324+
}
325+
if !self.inner.display_extensions.contains(color_space.egl_extension_name()) {
326+
return Err(ErrorKind::NotSupported(color_space.egl_extension_name()).into());
327+
}
190328
}
191329

192330
// Push `egl::NONE` to terminate the list.
@@ -304,17 +442,25 @@ impl<T: SurfaceTypeTrait> Surface<T> {
304442
/// # Safety
305443
///
306444
/// The caller must ensure that the attribute could be present.
307-
unsafe fn raw_attribute(&self, attr: EGLint) -> EGLint {
308-
unsafe {
309-
let mut value = 0;
310-
self.display.inner.egl.QuerySurface(
311-
*self.display.inner.raw,
312-
self.raw,
313-
attr,
314-
&mut value,
315-
);
316-
value
445+
pub unsafe fn raw_attribute(&self, attr: EGLint) -> EGLint {
446+
let mut value = 0;
447+
let success = unsafe {
448+
self.display.inner.egl.QuerySurface(*self.display.inner.raw, self.raw, attr, &mut value)
449+
};
450+
if success != 1 {
451+
eprintln!("Could not read Attrib {attr:#0x} from {:?}", self)
452+
}
453+
value
454+
}
455+
456+
/// Returns the [`ColorSpace`] of the [`Surface`], or [`None`] if `EGL_KHR_gl_colorspace` is not
457+
/// supported or the returned value is not recognized by [`ColorSpace`].
458+
pub fn color_space(&self) -> Option<ColorSpace> {
459+
if !self.display.inner.display_extensions.contains("EGL_KHR_gl_colorspace") {
460+
return None;
317461
}
462+
let color_space = unsafe { self.raw_attribute(egl::GL_COLORSPACE as EGLint) };
463+
ColorSpace::from_egl_colorspace(color_space as EGLenum)
318464
}
319465
}
320466

0 commit comments

Comments
 (0)