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 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 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 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
283pub 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 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 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_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 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 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 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 true
501 }
502 else {
503 false
504 }
505 }
506
507}