cognitive_graphics/
egl_tools.rs

1// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of
2// the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/
3
4//! This module contains common EGL-related tools.
5
6// -------------------------------------------------------------------------------------------------
7
8use libc;
9use std;
10use egl;
11
12use errors::GraphicsError;
13use attributes::{DmabufAttributes, EglAttributes};
14
15// -------------------------------------------------------------------------------------------------
16
17/// Raw hardware image.
18pub type RawHwImage = *const std::os::raw::c_void;
19
20// -------------------------------------------------------------------------------------------------
21
22/// Wrapper for hardware image. Conveying its size and adding ability to send between threads.
23#[derive(Clone, Debug)]
24pub struct HwImage {
25    image: RawHwImage,
26    width: usize,
27    height: usize,
28}
29
30/// `HwImage` contains only simple data and handle to raw image. It is as safe to send it as to use.
31unsafe impl Send for HwImage {}
32
33// -------------------------------------------------------------------------------------------------
34
35impl HwImage {
36    /// Constructs new `HwImage`.
37    pub fn new(image: RawHwImage, width: usize, height: usize) -> Self {
38        HwImage {
39            image: image,
40            width: width,
41            height: height,
42        }
43    }
44
45    /// Returns raw image.
46    pub fn as_raw(&self) -> RawHwImage {
47        self.image
48    }
49
50    /// Returns width of the image.
51    pub fn get_width(&self) -> usize {
52        self.width
53    }
54
55    /// Returns height of the image.
56    pub fn get_height(&self) -> usize {
57        self.height
58    }
59}
60
61// -------------------------------------------------------------------------------------------------
62
63/// Module with some constants for extensions.
64pub mod ext {
65    use egl;
66    use super::RawHwImage;
67
68    // Extension names
69    pub const IMAGE_BASE_EXT: &'static str = "EGL_KHR_image_base";
70    pub const IMAGE_EXTERNAL_EXT: &'static str = "GL_OES_EGL_image_external";
71
72    pub const DRM_BUFFER_USE_SHARE_MESA: egl::EGLint = 0x00000002;
73    pub const OPENGL_ES3_BIT_KHR: egl::EGLint = 0x00000040;
74
75    pub const DRM_BUFFER_FORMAT_MESA: egl::EGLint = 0x31D0;
76    pub const DRM_BUFFER_USE_MESA: egl::EGLint = 0x31D1;
77    pub const DRM_BUFFER_FORMAT_ARGB32_MESA: egl::EGLint = 0x31D2;
78    pub const DRM_BUFFER_MESA: egl::EGLenum = 0x31D3;
79    pub const DRM_BUFFER_STRIDE_MESA: egl::EGLint = 0x31D4;
80
81    pub const PLATFORM_GBM_KHR: egl::EGLenum = 0x31D7;
82
83    pub const LINUX_DMA_BUF_EXT: egl::EGLenum = 0x3270;
84    pub const LINUX_DRM_FOURCC_EXT: egl::EGLint = 0x3271;
85    pub const DMA_BUF_PLANE0_FD_EXT: egl::EGLint = 0x3272;
86    pub const DMA_BUF_PLANE0_OFFSET_EXT: egl::EGLint = 0x3273;
87    pub const DMA_BUF_PLANE0_PITCH_EXT: egl::EGLint = 0x3274;
88
89    /// Indicates image creation failure.
90    pub const NO_IMAGE: RawHwImage = 0 as RawHwImage;
91}
92
93// -------------------------------------------------------------------------------------------------
94
95/// List of attributes for create of configuration.
96#[cfg_attr(rustfmt, rustfmt_skip)]
97const CONFIG_ATTRIB_LIST: [egl::EGLint; 13] = [
98        egl::EGL_RENDERABLE_TYPE, egl::EGL_OPENGL_ES2_BIT,
99        egl::EGL_SURFACE_TYPE,    egl::EGL_WINDOW_BIT,
100        egl::EGL_RED_SIZE,        1,
101        egl::EGL_GREEN_SIZE,      1,
102        egl::EGL_BLUE_SIZE,       1,
103        egl::EGL_ALPHA_SIZE,      1,
104        egl::EGL_NONE
105    ];
106
107/// List of attributes for create of context.
108const CONTEXT_ATTRIB_LIST: [egl::EGLint; 3] = [egl::EGL_CONTEXT_CLIENT_VERSION, 2, egl::EGL_NONE];
109
110/// List of attributes for create of surface.
111const SURFACE_ATTRIB_LIST: [egl::EGLint; 0] = [];
112
113// -------------------------------------------------------------------------------------------------
114
115/// Type definition for `eglGetPlatformDisplayEXT` function.
116pub type GetPlatformDisplayFn = extern "C" fn(egl::EGLenum,
117                                              egl::EGLNativeDisplayType,
118                                              *const egl::EGLint)
119                                              -> egl::EGLDisplay;
120
121/// Type definition for `eglGetPlatformDisplayEXT` function.
122pub type CreatePlatformSurfaceFn = extern "C" fn(egl::EGLDisplay,
123                                                 egl::EGLConfig,
124                                                 egl::EGLNativeWindowType,
125                                                 *const egl::EGLint)
126                                                 -> egl::EGLSurface;
127
128/// Type definition for `eglCreateImageKHR` function.
129pub type CreateImageKhrFn = extern "C" fn(egl::EGLDisplay,
130                                          egl::EGLContext,
131                                          egl::EGLenum,
132                                          egl::EGLClientBuffer,
133                                          *const egl::EGLint)
134                                          -> RawHwImage;
135
136/// Type definition for `eglCreateImageKHR` function.
137pub type DestroyImageKhrFn = extern "C" fn(egl::EGLDisplay, RawHwImage) -> egl::EGLBoolean;
138
139/// Type definition for `eglCreateDRMImageMESA` function.
140pub type CreateDrmImageMesaFn = extern "C" fn(egl::EGLDisplay, *const egl::EGLint) -> RawHwImage;
141
142/// Type definition for `eglExportDRMImageMESA` function.
143pub type ExportDrmImageFn = extern "C" fn(egl::EGLDisplay,
144                                          RawHwImage,
145                                          *mut egl::EGLint,
146                                          *mut egl::EGLint,
147                                          *mut egl::EGLint)
148                                          -> egl::EGLBoolean;
149
150/// Type definition for `glEGLImageTargetTexture2DOES` function.
151pub type ImageTargetTexture2DOesFn = extern "C" fn(egl::EGLenum, RawHwImage);
152
153/// Type definition for `glEGLImageTargetRenderbufferStorageOES` function.
154pub type ImageTargetRenderStorageFn = extern "C" fn(egl::EGLenum, RawHwImage);
155
156// -------------------------------------------------------------------------------------------------
157
158/// Returns address of extension function.
159pub fn get_proc_addr_of_get_platform_display() -> Option<GetPlatformDisplayFn> {
160    unsafe {
161        let func = egl::get_proc_address("eglGetPlatformDisplayEXT") as *const ();
162        if !func.is_null() {
163            Some(std::mem::transmute::<_, GetPlatformDisplayFn>(func))
164        } else {
165            None
166        }
167    }
168}
169
170/// Returns address of extension function.
171pub fn get_proc_addr_of_create_platform_surface() -> Option<CreatePlatformSurfaceFn> {
172    unsafe {
173        let func = egl::get_proc_address("eglCreatePlatformWindowSurfaceEXT") as *const ();
174        if !func.is_null() {
175            Some(std::mem::transmute::<_, CreatePlatformSurfaceFn>(func))
176        } else {
177            None
178        }
179    }
180}
181
182/// Returns address of extension function.
183pub fn get_proc_addr_of_create_image_khr() -> Option<CreateImageKhrFn> {
184    unsafe {
185        let func = egl::get_proc_address("eglCreateImageKHR") as *const ();
186        if !func.is_null() {
187            Some(std::mem::transmute::<_, CreateImageKhrFn>(func))
188        } else {
189            None
190        }
191    }
192}
193
194/// Returns address of extension function.
195pub fn get_proc_addr_of_destroy_image_khr() -> Option<DestroyImageKhrFn> {
196    unsafe {
197        let func = egl::get_proc_address("eglDestroyImageKHR") as *const ();
198        if !func.is_null() {
199            Some(std::mem::transmute::<_, DestroyImageKhrFn>(func))
200        } else {
201            None
202        }
203    }
204}
205
206/// Returns address of extension function.
207pub fn get_proc_addr_of_create_drm_image_mesa() -> Option<CreateDrmImageMesaFn> {
208    unsafe {
209        let func = egl::get_proc_address("eglCreateDRMImageMESA") as *const ();
210        if !func.is_null() {
211            Some(std::mem::transmute::<_, CreateDrmImageMesaFn>(func))
212        } else {
213            None
214        }
215    }
216}
217
218/// Returns address of extension function.
219pub fn get_proc_addr_of_export_drm_image_mesa() -> Option<ExportDrmImageFn> {
220    unsafe {
221        let func = egl::get_proc_address("eglExportDRMImageMESA") as *const ();
222        if !func.is_null() {
223            Some(std::mem::transmute::<_, ExportDrmImageFn>(func))
224        } else {
225            None
226        }
227    }
228}
229
230/// Returns address of extension function.
231pub fn get_proc_addr_of_image_target_texture_2d_oes() -> Option<ImageTargetTexture2DOesFn> {
232    unsafe {
233        let func = egl::get_proc_address("glEGLImageTargetTexture2DOES") as *const ();
234        if !func.is_null() {
235            Some(std::mem::transmute::<_, ImageTargetTexture2DOesFn>(func))
236        } else {
237            None
238        }
239    }
240}
241
242/// Returns address of extension function.
243pub fn get_proc_addr_of_image_target_render_storage_oes() -> Option<ImageTargetRenderStorageFn> {
244    unsafe {
245        let func = egl::get_proc_address("glEGLImageTargetRenderbufferStorageOES") as *const ();
246        if !func.is_null() {
247            Some(std::mem::transmute::<_, ImageTargetRenderStorageFn>(func))
248        } else {
249            None
250        }
251    }
252}
253
254// -------------------------------------------------------------------------------------------------
255
256/// Gets GBM display.
257///
258/// First tries `eglGetDisplay`. If that fails, tries `eglGetPlatformDisplayEXT`.
259pub fn get_gbm_display(native_display: egl::EGLNativeDisplayType)
260                       -> Result<egl::EGLDisplay, GraphicsError> {
261    if let Some(display) = egl::get_display(native_display) {
262        Ok(display)
263    } else {
264        if let Some(get_platform_display) = self::get_proc_addr_of_get_platform_display() {
265            let display =
266                get_platform_display(ext::PLATFORM_GBM_KHR, native_display, std::ptr::null());
267            if !display.is_null() {
268                Ok(display)
269            } else {
270                Err(GraphicsError::new(format!("Failed to get EGL display")))
271            }
272        } else {
273            Err(GraphicsError::new(format!("GBM platform is not supported")))
274        }
275    }
276}
277
278// -------------------------------------------------------------------------------------------------
279
280/// Returns true if extension is available, false otherwise.
281pub fn has_extension(display: egl::EGLDisplay, extension: &str) -> bool {
282    if let Some(extensions) = egl::query_string(display, egl::EGL_EXTENSIONS) {
283        if let Ok(extensions) = extensions.to_owned().into_string() {
284            extensions.contains(extension)
285        } else {
286            false
287        }
288    } else {
289        false
290    }
291}
292
293// -------------------------------------------------------------------------------------------------
294
295/// Creates EGL image from given parameters.
296pub fn create_image(display: egl::EGLDisplay, attrs: &EglAttributes) -> Option<HwImage> {
297    if let Some(create_image) = get_proc_addr_of_create_image_khr() {
298        // Create attributes
299        let mut attribs = [egl::EGL_NONE; 9];
300
301        attribs[0] = egl::EGL_WIDTH;
302        attribs[1] = attrs.width;
303        attribs[2] = egl::EGL_HEIGHT;
304        attribs[3] = attrs.height;
305        attribs[4] = ext::DRM_BUFFER_STRIDE_MESA;
306        attribs[5] = (attrs.stride / 4) as egl::EGLint;
307        attribs[6] = ext::DRM_BUFFER_FORMAT_MESA;
308        attribs[7] = ext::DRM_BUFFER_FORMAT_ARGB32_MESA;
309        attribs[8] = egl::EGL_NONE;
310
311        // Create image
312        let img = create_image(display,
313                               egl::EGL_NO_CONTEXT,
314                               ext::DRM_BUFFER_MESA,
315                               attrs.name as *mut libc::c_void,
316                               (&attribs) as *const egl::EGLint);
317
318        if img != ext::NO_IMAGE {
319            Some(HwImage::new(img, attrs.width as usize, attrs.height as usize))
320        } else {
321            None
322        }
323    } else {
324        None
325    }
326}
327
328// -------------------------------------------------------------------------------------------------
329
330/// Imports dmabuf as EGL image.
331pub fn import_dmabuf(display: egl::EGLDisplay, attrs: &DmabufAttributes) -> Option<HwImage> {
332    if let Some(create_image) = get_proc_addr_of_create_image_khr() {
333        // Create attributes
334        let mut attribs = [egl::EGL_NONE; 25];
335
336        attribs[0] = egl::EGL_WIDTH;
337        attribs[1] = attrs.width;
338        attribs[2] = egl::EGL_HEIGHT;
339        attribs[3] = attrs.height;
340        attribs[4] = ext::LINUX_DRM_FOURCC_EXT;
341        attribs[5] = attrs.format as egl::EGLint;
342
343        for i in 0..attrs.get_num_of_planes() {
344            let idx = 5 + (6 * i);
345            attribs[idx + 1] = ext::DMA_BUF_PLANE0_FD_EXT;
346            attribs[idx + 2] = attrs.planes[i].fd;
347            attribs[idx + 3] = ext::DMA_BUF_PLANE0_OFFSET_EXT;
348            attribs[idx + 4] = attrs.planes[i].offset as egl::EGLint;
349            attribs[idx + 5] = ext::DMA_BUF_PLANE0_PITCH_EXT;
350            attribs[idx + 6] = attrs.planes[i].stride as egl::EGLint;
351        }
352
353        // Create image
354        let img = create_image(display,
355                               egl::EGL_NO_CONTEXT,
356                               ext::LINUX_DMA_BUF_EXT,
357                               std::ptr::null_mut(),
358                               (&attribs) as *const egl::EGLint);
359
360        if img != ext::NO_IMAGE {
361            Some(HwImage::new(img, attrs.width as usize, attrs.height as usize))
362        } else {
363            None
364        }
365    } else {
366        None
367    }
368}
369
370// -------------------------------------------------------------------------------------------------
371
372/// Destroys given EGL image.
373pub fn destroy_image(display: egl::EGLDisplay, image: HwImage) -> Result<(), ()> {
374    if let Some(destroy_image) = get_proc_addr_of_destroy_image_khr() {
375        // Create image
376        let result = destroy_image(display, image.as_raw());
377        if result == egl::EGL_TRUE {
378            Ok(())
379        } else {
380            Err(())
381        }
382    } else {
383        Err(())
384    }
385}
386
387// -------------------------------------------------------------------------------------------------
388
389/// This structure collects EGL-related data.
390#[derive(Clone, Copy)]
391pub struct EglBucket {
392    pub display: egl::EGLDisplay,
393    pub config: egl::EGLConfig,
394    pub context: egl::EGLContext,
395    pub surface: egl::EGLSurface,
396}
397
398// -------------------------------------------------------------------------------------------------
399
400impl EglBucket {
401    /// Destroys surface, context and terminates display.
402    pub fn destroy(self) {
403        egl::destroy_surface(self.display, self.surface);
404        egl::destroy_context(self.display, self.context);
405        egl::terminate(self.display);
406    }
407}
408
409// -------------------------------------------------------------------------------------------------
410
411/// This structure is returned by `EglBucket::make_current` and is used to automatically release
412/// EGL context when this structure goes out of the scope.
413pub struct EglContext {
414    egl: EglBucket,
415}
416
417// -------------------------------------------------------------------------------------------------
418
419impl EglBucket {
420    /// `EglBucket` constructor.
421    pub fn new(native_display: egl::EGLNativeDisplayType,
422               window_type: egl::EGLNativeWindowType)
423               -> Result<Self, GraphicsError> {
424        // Get display
425        let display = self::get_gbm_display(native_display)?;
426
427        // Initialize EGL
428        let mut major = 0;
429        let mut minor = 0;
430        if !egl::initialize(display, &mut major, &mut minor) {
431            return Err(GraphicsError::new(format!("Failed to initialize EGL")));
432        };
433
434        if !egl::bind_api(egl::EGL_OPENGL_ES_API) {
435            return Err(GraphicsError::new(format!("Failed to bind EGL API")));
436        };
437
438        // Choose config
439        let config = if let Some(config) = egl::choose_config(display, &CONFIG_ATTRIB_LIST, 1) {
440            config
441        } else {
442            return Err(GraphicsError::new(format!("Failed to choose EGL config")));
443        };
444
445        // Create context
446        let c = egl::create_context(display, config, egl::EGL_NO_CONTEXT, &CONTEXT_ATTRIB_LIST);
447        let context = if let Some(context) = c {
448            context
449        } else {
450            return Err(GraphicsError::new(format!("Failed to create EGL context")));
451        };
452
453        // Create window surface
454        let s = egl::create_window_surface(display, config, window_type, &SURFACE_ATTRIB_LIST);
455        let surface = if let Some(surface) = s {
456            surface
457        } else {
458            return Err(GraphicsError::new(format!("Failed to create EGL window surface")));
459        };
460
461        // Return bundle
462        Ok(EglBucket {
463               display: display,
464               config: config,
465               context: context,
466               surface: surface,
467           })
468    }
469
470    /// Make EGL context current.
471    /// This method returns `EglContext` structure which will release context when goes out of the
472    /// scope.
473    pub fn make_current(&self) -> Result<EglContext, GraphicsError> {
474        if !egl::make_current(self.display, self.surface, self.surface, self.context) {
475            Err(GraphicsError::new(format!("Failed to make EGL context current")))
476        } else {
477            Ok(EglContext::new(*self))
478        }
479    }
480}
481
482// -------------------------------------------------------------------------------------------------
483
484impl EglContext {
485    /// `EglContext` constructor.
486    fn new(egl: EglBucket) -> Self {
487        EglContext { egl: egl }
488    }
489}
490
491// -------------------------------------------------------------------------------------------------
492
493impl EglContext {
494    /// Release EGL context.
495    fn release(&self) -> Result<(), GraphicsError> {
496        if !egl::make_current(self.egl.display,
497                              egl::EGL_NO_SURFACE,
498                              egl::EGL_NO_SURFACE,
499                              egl::EGL_NO_CONTEXT) {
500            Err(GraphicsError::new(format!("Failed to release EGL context")))
501        } else {
502            Ok(())
503        }
504    }
505
506    /// Swap buffers.
507    pub fn swap_buffers(&self) -> Result<(), GraphicsError> {
508        if egl::swap_buffers(self.egl.display, self.egl.surface) {
509            Ok(())
510        } else {
511            Err(GraphicsError::new(format!("Failed to swap EGL buffers (0x{:x})", egl::get_error())))
512        }
513    }
514}
515
516// -------------------------------------------------------------------------------------------------
517
518impl Drop for EglContext {
519    fn drop(&mut self) {
520        self.release().expect("Failed to release EGL context");
521    }
522}
523
524// -------------------------------------------------------------------------------------------------