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