makepad_platform/os/linux/x11/
opengl_x11.rs

1use {
2    std::{
3        mem,
4        os::raw::{c_long, c_void},
5        ffi::CString,
6        os::{self, fd::{AsRawFd as _, FromRawFd as _, OwnedFd}},
7    },
8    self::super::{
9        x11_sys,
10        xlib_window::XlibWindow,
11    },
12    self::super::super::{
13        dma_buf,
14        egl_sys::{self, LibEgl},
15        gl_sys,
16        gl_sys::LibGl,
17    },
18    crate::{
19        cx::Cx,
20        window::WindowId,
21        makepad_math::DVec2,
22        pass::{PassClearColor, PassClearDepth, PassId},
23        event::*,
24        texture::{CxTexture, Texture},
25    },
26};
27
28impl Cx {
29    
30    pub fn draw_pass_to_window(
31        &mut self,
32        pass_id: PassId,
33        opengl_window: &mut OpenglWindow,
34    ) {
35        let draw_list_id = self.passes[pass_id].main_draw_list_id.unwrap();
36        
37        self.setup_render_pass(pass_id);
38        
39        let gl = self.os.gl();
40                
41        let egl_surface = opengl_window.egl_surface;
42        
43        self.passes[pass_id].paint_dirty = false;
44
45        let pix_width = opengl_window.window_geom.inner_size.x * opengl_window.window_geom.dpi_factor;
46        let pix_height = opengl_window.window_geom.inner_size.y * opengl_window.window_geom.dpi_factor;
47        unsafe {
48            let opengl_cx = self.os.opengl_cx.as_ref().unwrap();
49            (opengl_cx.libegl.eglMakeCurrent.unwrap())(opengl_cx.egl_display, egl_surface, egl_surface, opengl_cx.egl_context);
50            (gl.glViewport)(0, 0, pix_width.floor() as i32, pix_height.floor() as i32);
51        }
52        
53        let clear_color = if self.passes[pass_id].color_textures.len() == 0 {
54            self.passes[pass_id].clear_color 
55        }
56        else {
57            match self.passes[pass_id].color_textures[0].clear_color {
58                PassClearColor::InitWith(color) => color,
59                PassClearColor::ClearWith(color) => color
60            }
61        };
62        let clear_depth = match self.passes[pass_id].clear_depth {
63            PassClearDepth::InitWith(depth) => depth,
64            PassClearDepth::ClearWith(depth) => depth
65        };
66        
67        if !self.passes[pass_id].dont_clear {
68            unsafe {
69                (gl.glBindFramebuffer)(gl_sys::FRAMEBUFFER, 0);
70                (gl.glClearDepthf)(clear_depth as f32);
71                (gl.glClearColor)(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
72                (gl.glClear)(gl_sys::COLOR_BUFFER_BIT | gl_sys::DEPTH_BUFFER_BIT);
73            }
74        }
75        Self::set_default_depth_and_blend_mode(self.os.gl());
76        
77        let mut zbias = 0.0;
78        let zbias_step = self.passes[pass_id].zbias_step;
79        
80        self.render_view(
81            pass_id,
82            draw_list_id,
83            &mut zbias,
84            zbias_step,
85        );
86
87        unsafe {
88            let opengl_cx = self.os.opengl_cx.as_ref().unwrap();
89            (opengl_cx.libegl.eglSwapBuffers.unwrap())(opengl_cx.egl_display, egl_surface);
90        }
91    }
92
93    pub fn share_texture_for_presentable_image(
94        &mut self,
95        texture: &Texture,
96    ) -> dma_buf::Image<OwnedFd> {
97        let cxtexture = &mut self.textures[texture.texture_id()];
98        cxtexture.update_shared_texture(self.os.gl());
99
100        let opengl_cx = self.os.opengl_cx.as_ref().unwrap();
101        unsafe {
102            let egl_image = (opengl_cx.libegl.eglCreateImageKHR.unwrap())(
103                opengl_cx.egl_display,
104                opengl_cx.egl_context,
105                egl_sys::EGL_GL_TEXTURE_2D_KHR,
106                cxtexture.os.gl_texture.unwrap() as egl_sys::EGLClientBuffer,
107                std::ptr::null(),
108            );
109            assert!(!egl_image.is_null(), "eglCreateImageKHR failed");
110
111            let (mut fourcc, mut num_planes) = (0, 0);
112            assert!(
113                (
114                    opengl_cx.libegl.eglExportDMABUFImageQueryMESA
115                        .expect("eglExportDMABUFImageQueryMESA unsupported")
116                )(
117                    opengl_cx.egl_display,
118                    egl_image,
119                    &mut fourcc as *mut u32 as *mut i32,
120                    &mut num_planes,
121                    std::ptr::null_mut(),
122                ) != 0,
123                "eglExportDMABUFImageQueryMESA failed",
124            );
125            assert!(
126                num_planes == 1,
127                "planar DRM format {:?} ({fourcc:#x}) unsupported (num_planes={num_planes})",
128                std::str::from_utf8(&u32::to_le_bytes(fourcc))
129            );
130
131            // HACK(eddyb) `modifiers` are reported per-plane, so to avoid UB,
132            // a second query call is used *after* the `num_planes == 1` check.
133            let mut modifiers = 0;
134            assert!(
135                (opengl_cx.libegl.eglExportDMABUFImageQueryMESA.unwrap())(
136                    opengl_cx.egl_display,
137                    egl_image,
138                    std::ptr::null_mut(),
139                    std::ptr::null_mut(),
140                    &mut modifiers,
141                ) != 0,
142                "eglExportDMABUFImageQueryMESA failed",
143            );
144
145            let (mut dma_buf_fd, mut offset, mut stride) = (0, 0, 0);
146            assert!(
147                (opengl_cx.libegl.eglExportDMABUFImageMESA.unwrap())(
148                    opengl_cx.egl_display,
149                    egl_image,
150                    &mut dma_buf_fd,
151                    &mut stride as *mut u32 as *mut i32,
152                    &mut offset as *mut u32 as *mut i32,
153                ) != 0,
154                "eglExportDMABUFImageMESA failed",
155            );
156
157            assert!(
158                (opengl_cx.libegl.eglDestroyImageKHR.unwrap())(
159                    opengl_cx.egl_display,
160                    egl_image,
161                ) != 0,
162                "eglDestroyImageKHR failed",
163            );
164
165            dma_buf::Image {
166                drm_format: dma_buf::DrmFormat {
167                    fourcc,
168                    modifiers,
169                },
170                planes: dma_buf::ImagePlane {
171                    dma_buf_fd: os::fd::OwnedFd::from_raw_fd(dma_buf_fd),
172                    offset,
173                    stride,
174                },
175            }
176        }
177    }
178}
179
180
181impl CxTexture {
182    fn update_shared_texture(&mut self, gl:&LibGl) {
183        if !self.alloc_shared(){
184            return
185        }
186        let alloc = self.alloc.as_ref().unwrap();
187        
188        // HACK(eddyb) drain error queue, so that we can check erors below.
189        while unsafe { (gl.glGetError)() } != 0 {}
190
191        unsafe {
192            if self.os.gl_texture.is_none() {
193                let mut gl_texture = std::mem::MaybeUninit::uninit();
194                (gl.glGenTextures)(1, gl_texture.as_mut_ptr());
195                self.os.gl_texture = Some(gl_texture.assume_init());
196            }
197
198            (gl.glBindTexture)(gl_sys::TEXTURE_2D, self.os.gl_texture.unwrap());
199
200            (gl.glTexParameteri)(gl_sys::TEXTURE_2D, gl_sys::TEXTURE_MIN_FILTER, gl_sys::NEAREST as i32);
201            (gl.glTexParameteri)(gl_sys::TEXTURE_2D, gl_sys::TEXTURE_MAG_FILTER, gl_sys::NEAREST as i32);
202            (gl.glTexImage2D)(
203                gl_sys::TEXTURE_2D,
204                0,
205                gl_sys::RGBA as i32,
206                alloc.width as i32,
207                alloc.height as i32,
208                0,
209                gl_sys::RGBA,
210                gl_sys::UNSIGNED_BYTE,
211                std::ptr::null()
212            );
213            assert_eq!((gl.glGetError)(), 0, "glTexImage2D({}, {}) failed", alloc.width, alloc.height);
214            (gl.glBindTexture)(gl_sys::TEXTURE_2D, 0);
215        }
216    }
217
218    pub fn update_from_shared_dma_buf_image(
219        &mut self,
220        gl:&LibGl,
221        opengl_cx: &OpenglCx,
222        dma_buf_image: &dma_buf::Image<os::fd::OwnedFd>,
223    ) {
224        if !self.alloc_shared(){
225            return
226        }
227        let alloc = self.alloc.as_ref().unwrap();
228
229        // HACK(eddyb) drain error queue, so that we can check erors below.
230        while unsafe { (gl.glGetError)() } != 0 {}
231        opengl_cx.make_current();
232        while unsafe { (gl.glGetError)() } != 0 {}
233
234        let dma_buf::Image { drm_format, planes: ref plane0 } = *dma_buf_image;
235
236        let image_attribs = [
237            egl_sys::EGL_LINUX_DRM_FOURCC_EXT,
238            drm_format.fourcc,
239            egl_sys::EGL_WIDTH,
240            alloc.width as u32,
241            egl_sys::EGL_HEIGHT,
242            alloc.height as u32,
243            egl_sys::EGL_DMA_BUF_PLANE0_FD_EXT,
244            plane0.dma_buf_fd.as_raw_fd() as u32,
245            egl_sys::EGL_DMA_BUF_PLANE0_OFFSET_EXT,
246            plane0.offset,
247            egl_sys::EGL_DMA_BUF_PLANE0_PITCH_EXT,
248            plane0.stride,
249            egl_sys::EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT,
250            drm_format.modifiers as u32,
251            egl_sys::EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT,
252            (drm_format.modifiers >> 32) as u32,
253            egl_sys::EGL_NONE,
254        ];
255        let egl_image = unsafe { (opengl_cx.libegl.eglCreateImageKHR.unwrap())(
256            opengl_cx.egl_display,
257            std::ptr::null_mut(),
258            egl_sys::EGL_LINUX_DMA_BUF_EXT,
259            std::ptr::null_mut(),
260            image_attribs.as_ptr() as _,
261        ) };
262        assert!(!egl_image.is_null(), "eglCreateImageKHR failed");
263
264        unsafe {
265            let gl_texture = *self.os.gl_texture.get_or_insert_with(|| {
266                let mut gl_texture = std::mem::MaybeUninit::uninit();
267                (gl.glGenTextures)(1, gl_texture.as_mut_ptr());
268                assert_eq!((gl.glGetError)(), 0, "glGenTextures failed");
269                gl_texture.assume_init()
270            });
271
272            (gl.glBindTexture)(gl_sys::TEXTURE_2D, gl_texture);
273            assert_eq!((gl.glGetError)(), 0, "glBindTexture({gl_texture}) failed");
274
275            (opengl_cx.libegl.glEGLImageTargetTexture2DOES.unwrap())(gl_sys::TEXTURE_2D, egl_image);
276            assert_eq!((gl.glGetError)(), 0, "glEGLImageTargetTexture2DOES failed");
277
278            (gl.glBindTexture)(gl_sys::TEXTURE_2D, 0);
279        }
280    }
281}
282
283// FIXME(eddyb) move this out of `linux::x11`, since it's mostly generic EGL.
284pub struct OpenglCx {
285    libegl: LibEgl,
286    pub libgl: LibGl,
287    egl_display: egl_sys::EGLDisplay,
288    egl_config: egl_sys::EGLConfig,
289    egl_context: egl_sys::EGLContext,
290
291    egl_platform: egl_sys::EGLenum,
292    egl_platform_display: *mut c_void,
293}
294
295impl OpenglCx {
296    pub unsafe fn from_egl_platform_display<T>(
297        egl_platform: egl_sys::EGLenum,
298        egl_platform_display: *mut T,
299    ) -> OpenglCx {
300        let egl_platform_display = egl_platform_display as *mut c_void;
301
302        // Load EGL function pointers.
303        let libegl = LibEgl::try_load().expect("can't load LibEGL");
304
305        let mut major = 0;
306        let mut minor = 0;
307
308        let egl_display = (libegl.eglGetPlatformDisplayEXT.unwrap())(
309            egl_platform,
310            egl_platform_display,
311            std::ptr::null(),
312        );
313        assert!(!egl_display.is_null(), "can't get EGL platform display");
314
315        assert!(
316            (libegl.eglInitialize.unwrap())(egl_display, &mut major, &mut minor) != 0,
317            "can't initialize EGL",
318        );
319
320        assert!(
321            (libegl.eglBindAPI.unwrap())(egl_sys::EGL_OPENGL_ES_API) != 0,
322            "can't bind EGL_OPENGL_ES_API",
323        );
324
325        // Choose framebuffer configuration.
326        let cfg_attribs = [
327            egl_sys::EGL_RED_SIZE,
328            8,
329            egl_sys::EGL_GREEN_SIZE,
330            8,
331            egl_sys::EGL_BLUE_SIZE,
332            8,
333            egl_sys::EGL_ALPHA_SIZE,
334            8,
335            // egl_sys::EGL_DEPTH_SIZE,
336            // 24,
337            // egl_sys::EGL_STENCIL_SIZE,
338            // 8,
339            egl_sys::EGL_RENDERABLE_TYPE,
340            egl_sys::EGL_OPENGL_ES2_BIT,
341            egl_sys::EGL_NONE
342        ];
343
344        let mut egl_config = 0 as egl_sys::EGLConfig;
345        let mut matched_egl_configs = 0;
346        assert!(
347            (libegl.eglChooseConfig.unwrap())(
348                egl_display,
349                cfg_attribs.as_ptr() as _,
350                &mut egl_config,
351                1,
352                &mut matched_egl_configs
353            ) != 0 && matched_egl_configs == 1,
354            "eglChooseConfig failed",
355        );
356
357        // Create EGL context.
358        let ctx_attribs = [
359            egl_sys::EGL_CONTEXT_MAJOR_VERSION,
360            #[cfg(use_gles_3)]
361            3,
362            #[cfg(not(use_gles_3))]
363            2,
364            egl_sys::EGL_NONE
365        ];
366
367        let egl_context = (libegl.eglCreateContext.unwrap())(
368            egl_display,
369            egl_config,
370            egl_sys::EGL_NO_CONTEXT,
371            ctx_attribs.as_ptr() as _,
372        );
373        assert!(!egl_context.is_null(), "eglCreateContext failed");
374        
375        let libgl = LibGl::try_load(| s | {
376            for s in s{
377                let s = CString::new(*s).unwrap();
378                let p = unsafe{libegl.eglGetProcAddress.unwrap()(s.as_ptr())};
379                if !p.is_null(){
380                    return p
381                }
382            }
383            0 as * const _
384        }).expect("Cant load openGL functions");
385        
386        OpenglCx {
387            libegl,
388            libgl,
389            egl_display,
390            egl_config,
391            egl_context,
392
393            egl_platform,
394            egl_platform_display,
395        }
396    }
397
398    pub fn make_current(&self) {
399        unsafe {
400            (self.libegl.eglMakeCurrent.unwrap())(
401                self.egl_display,
402                egl_sys::EGL_NO_SURFACE,
403                egl_sys::EGL_NO_SURFACE,
404                self.egl_context,
405            );
406        }
407    }
408}
409
410#[derive(Clone)]
411pub struct OpenglWindow {
412    pub first_draw: bool,
413    pub window_id: WindowId,
414    pub window_geom: WindowGeom,
415    pub opening_repaint_count: u32,
416    pub cal_size: DVec2,
417    pub xlib_window: Box<XlibWindow>,
418    pub egl_surface: egl_sys::EGLSurface,
419}
420
421impl OpenglWindow {
422    pub fn new(
423        window_id: WindowId,
424        opengl_cx: &OpenglCx,
425        inner_size: DVec2,
426        position: Option<DVec2>,
427        title: &str
428    ) -> OpenglWindow {
429        // Checked "downcast" of the EGL platform display to a X11 display.
430        assert_eq!(opengl_cx.egl_platform, egl_sys::EGL_PLATFORM_X11_EXT);
431        let display = opengl_cx. egl_platform_display as *mut x11_sys::Display;
432
433        let mut xlib_window = Box::new(XlibWindow::new(window_id));
434
435        // Get X11 visual from EGL configuration.
436        let visual_info = unsafe {
437            let mut native_visual_id = 0;
438            assert!(
439                (opengl_cx.libegl.eglGetConfigAttrib.unwrap())(
440                    opengl_cx.egl_display,
441                    opengl_cx.egl_config,
442                    egl_sys::EGL_NATIVE_VISUAL_ID as _,
443                    &mut native_visual_id,
444                ) != 0,
445                "eglGetConfigAttrib(EGL_NATIVE_VISUAL_ID) failed",
446            );
447
448            let mut visual_template = mem::zeroed::<x11_sys::XVisualInfo>();
449            visual_template.visualid = native_visual_id as _;
450
451            let mut count = 0;
452            let visual_info_ptr = x11_sys::XGetVisualInfo(
453                display,
454                x11_sys::VisualIDMask as c_long,
455                &mut visual_template,
456                &mut count,
457            );
458            assert!(
459                !visual_info_ptr.is_null() && count == 1,
460                "can't get visual from EGL configuration with XGetVisualInfo",
461            );
462
463            let visual_info = *visual_info_ptr;
464            x11_sys::XFree(visual_info_ptr as *mut c_void);
465            visual_info
466        };
467
468        let custom_window_chrome = false;
469        xlib_window.init(title, inner_size, position, visual_info, custom_window_chrome);
470
471        let egl_surface = unsafe {
472            (opengl_cx.libegl.eglCreateWindowSurface.unwrap())(
473                opengl_cx.egl_display,
474                opengl_cx.egl_config,
475                xlib_window.window.unwrap(),
476                std::ptr::null(),
477            )
478        };
479        assert!(!egl_surface.is_null(), "eglCreateWindowSurface failed");
480
481        OpenglWindow {
482            first_draw: true,
483            window_id,
484            opening_repaint_count: 0,
485            cal_size: DVec2::default(),
486            window_geom: xlib_window.get_window_geom(),
487            xlib_window,
488            egl_surface,
489        }
490    }
491    
492    pub fn resize_buffers(&mut self) -> bool {
493        let cal_size = DVec2 {
494            x: self.window_geom.inner_size.x * self.window_geom.dpi_factor,
495            y: self.window_geom.inner_size.y * self.window_geom.dpi_factor
496        };
497        if self.cal_size != cal_size {
498            self.cal_size = cal_size;
499            // resize the framebuffer
500            true
501        }
502        else {
503            false
504        }
505    }
506    
507}