1use libc;
9use std;
10use egl;
11
12use errors::GraphicsError;
13use attributes::{DmabufAttributes, EglAttributes};
14
15pub type RawHwImage = *const std::os::raw::c_void;
19
20#[derive(Clone, Debug)]
24pub struct HwImage {
25 image: RawHwImage,
26 width: usize,
27 height: usize,
28}
29
30unsafe impl Send for HwImage {}
32
33impl HwImage {
36 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 pub fn as_raw(&self) -> RawHwImage {
47 self.image
48 }
49
50 pub fn get_width(&self) -> usize {
52 self.width
53 }
54
55 pub fn get_height(&self) -> usize {
57 self.height
58 }
59}
60
61pub mod ext {
65 use egl;
66 use super::RawHwImage;
67
68 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 pub const NO_IMAGE: RawHwImage = 0 as RawHwImage;
91}
92
93#[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
107const CONTEXT_ATTRIB_LIST: [egl::EGLint; 3] = [egl::EGL_CONTEXT_CLIENT_VERSION, 2, egl::EGL_NONE];
109
110const SURFACE_ATTRIB_LIST: [egl::EGLint; 0] = [];
112
113pub type GetPlatformDisplayFn = extern "C" fn(egl::EGLenum,
117 egl::EGLNativeDisplayType,
118 *const egl::EGLint)
119 -> egl::EGLDisplay;
120
121pub type CreatePlatformSurfaceFn = extern "C" fn(egl::EGLDisplay,
123 egl::EGLConfig,
124 egl::EGLNativeWindowType,
125 *const egl::EGLint)
126 -> egl::EGLSurface;
127
128pub type CreateImageKhrFn = extern "C" fn(egl::EGLDisplay,
130 egl::EGLContext,
131 egl::EGLenum,
132 egl::EGLClientBuffer,
133 *const egl::EGLint)
134 -> RawHwImage;
135
136pub type DestroyImageKhrFn = extern "C" fn(egl::EGLDisplay, RawHwImage) -> egl::EGLBoolean;
138
139pub type CreateDrmImageMesaFn = extern "C" fn(egl::EGLDisplay, *const egl::EGLint) -> RawHwImage;
141
142pub 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
150pub type ImageTargetTexture2DOesFn = extern "C" fn(egl::EGLenum, RawHwImage);
152
153pub type ImageTargetRenderStorageFn = extern "C" fn(egl::EGLenum, RawHwImage);
155
156pub 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
170pub 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
182pub 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
194pub 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
206pub 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
218pub 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
230pub 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
242pub 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
254pub 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
278pub 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
293pub fn create_image(display: egl::EGLDisplay, attrs: &EglAttributes) -> Option<HwImage> {
297 if let Some(create_image) = get_proc_addr_of_create_image_khr() {
298 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 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
328pub fn import_dmabuf(display: egl::EGLDisplay, attrs: &DmabufAttributes) -> Option<HwImage> {
332 if let Some(create_image) = get_proc_addr_of_create_image_khr() {
333 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 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
370pub fn destroy_image(display: egl::EGLDisplay, image: HwImage) -> Result<(), ()> {
374 if let Some(destroy_image) = get_proc_addr_of_destroy_image_khr() {
375 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#[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
398impl EglBucket {
401 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
409pub struct EglContext {
414 egl: EglBucket,
415}
416
417impl EglBucket {
420 pub fn new(native_display: egl::EGLNativeDisplayType,
422 window_type: egl::EGLNativeWindowType)
423 -> Result<Self, GraphicsError> {
424 let display = self::get_gbm_display(native_display)?;
426
427 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 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 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 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 Ok(EglBucket {
463 display: display,
464 config: config,
465 context: context,
466 surface: surface,
467 })
468 }
469
470 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
482impl EglContext {
485 fn new(egl: EglBucket) -> Self {
487 EglContext { egl: egl }
488 }
489}
490
491impl EglContext {
494 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 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
516impl Drop for EglContext {
519 fn drop(&mut self) {
520 self.release().expect("Failed to release EGL context");
521 }
522}
523
524