Skip to content

Commit 1b8ba9e

Browse files
committed
EGL: Provide color-space support
1 parent 23e4ca1 commit 1b8ba9e

File tree

16 files changed

+329
-89
lines changed

16 files changed

+329
-89
lines changed

glutin-winit/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,9 @@ impl DisplayBuilder {
9191
///
9292
/// # Api-specific
9393
///
94-
/// **WGL:** - [`WindowAttributes`] **must** be passed in
95-
/// [`Self::with_window_attributes()`] if modern OpenGL(ES) is desired,
96-
/// otherwise only builtin functions like `glClear` will be available.
94+
/// - **WGL:** [`WindowAttributes`] **must** be passed in
95+
/// [`Self::with_window_attributes()`] if modern OpenGL(ES) is desired,
96+
/// otherwise only builtin functions like `glClear()` will be available.
9797
pub fn build<Picker>(
9898
mut self,
9999
event_loop: &impl GlutinEventLoop,

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/device.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,9 @@ impl Device {
162162
return None;
163163
}
164164

165-
const EGL_DRM_RENDER_NODE_PATH_EXT: egl::types::EGLenum = 0x3377;
166165
// SAFETY: We pass a valid EGLDevice pointer, and validated that the enum name
167166
// is valid because the extension is present.
168-
unsafe { Self::query_string(self.raw_device(), EGL_DRM_RENDER_NODE_PATH_EXT) }
167+
unsafe { Self::query_string(self.raw_device(), egl::DRM_RENDER_NODE_FILE_EXT) }
169168
.map(Path::new)
170169
}
171170

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};
@@ -562,12 +564,15 @@ impl GlDisplay for Display {
562564
unsafe { Self::find_configs(self, template) }
563565
}
564566

567+
/// Creates a window surface without [`ColorSpace`]. Use
568+
/// [`Self::create_window_surface()`] if you wish to specify a color
569+
/// space.
565570
unsafe fn create_window_surface(
566571
&self,
567572
config: &Self::Config,
568573
surface_attributes: &SurfaceAttributes<WindowSurface>,
569574
) -> Result<Self::WindowSurface> {
570-
unsafe { Self::create_window_surface(self, config, surface_attributes) }
575+
unsafe { Self::create_window_surface(self, config, &surface_attributes.clone().into()) }
571576
}
572577

573578
unsafe fn create_pbuffer_surface(
@@ -586,12 +591,15 @@ impl GlDisplay for Display {
586591
unsafe { Self::create_context(self, config, context_attributes) }
587592
}
588593

594+
/// Creates a pixmap surface without [`ColorSpace`]. Use
595+
/// [`Self::create_pixmap_surface()`] if you wish to specify a color
596+
/// space.
589597
unsafe fn create_pixmap_surface(
590598
&self,
591599
config: &Self::Config,
592600
surface_attributes: &SurfaceAttributes<PixmapSurface>,
593601
) -> Result<Self::PixmapSurface> {
594-
unsafe { Self::create_pixmap_surface(self, config, surface_attributes) }
602+
unsafe { Self::create_pixmap_surface(self, config, &surface_attributes.clone().into()) }
595603
}
596604

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

glutin/src/api/egl/surface.rs

+177-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.
@@ -307,17 +445,26 @@ impl<T: SurfaceTypeTrait> Surface<T> {
307445
/// # Safety
308446
///
309447
/// The caller must ensure that the attribute could be present.
310-
unsafe fn raw_attribute(&self, attr: EGLint) -> EGLint {
311-
unsafe {
312-
let mut value = 0;
313-
self.display.inner.egl.QuerySurface(
314-
*self.display.inner.raw,
315-
self.raw,
316-
attr,
317-
&mut value,
318-
);
319-
value
448+
pub unsafe fn raw_attribute(&self, attr: EGLint) -> EGLint {
449+
let mut value = 0;
450+
let success = unsafe {
451+
self.display.inner.egl.QuerySurface(*self.display.inner.raw, self.raw, attr, &mut value)
452+
};
453+
if success != 1 {
454+
eprintln!("Could not read Attrib {attr:#0x} from {:?}", self)
455+
}
456+
value
457+
}
458+
459+
/// Returns the [`ColorSpace`] of the [`Surface`], or [`None`] if
460+
/// `EGL_KHR_gl_colorspace` is not supported or the returned value is
461+
/// not recognized by [`ColorSpace`].
462+
pub fn color_space(&self) -> Option<ColorSpace> {
463+
if !self.display.inner.display_extensions.contains("EGL_KHR_gl_colorspace") {
464+
return None;
320465
}
466+
let color_space = unsafe { self.raw_attribute(egl::GL_COLORSPACE as EGLint) };
467+
ColorSpace::from_egl_colorspace(color_space as EGLenum)
321468
}
322469
}
323470

0 commit comments

Comments
 (0)