From 897f45e49f9d5677e2b97502a1cfcf035cb22f36 Mon Sep 17 00:00:00 2001 From: modeco80 Date: Mon, 5 Aug 2024 22:17:32 -0400 Subject: [PATCH] cleanup EGL + OpenGL init into given passes --- .../retro_frontend/src/libretro_callbacks.rs | 1 - crates/retrovnc/src/egl.rs | 23 +++ crates/retrovnc/src/main.rs | 155 +++++++++--------- 3 files changed, 105 insertions(+), 74 deletions(-) diff --git a/crates/retro_frontend/src/libretro_callbacks.rs b/crates/retro_frontend/src/libretro_callbacks.rs index 4293e86..b9a5625 100644 --- a/crates/retro_frontend/src/libretro_callbacks.rs +++ b/crates/retro_frontend/src/libretro_callbacks.rs @@ -247,7 +247,6 @@ pub(crate) unsafe extern "C" fn video_refresh_callback( .resize((pitch * height as usize) as usize, 0); } - // TODO: Make this convert from weird pitches to native resolution where possible. for x in 0..pitch as usize { for y in 0..height as usize { let rgb = Rgb565::from_rgb565(pixel_data_slice[y * pitch as usize + x]); diff --git a/crates/retrovnc/src/egl.rs b/crates/retrovnc/src/egl.rs index dfd04ea..3ae2e35 100644 --- a/crates/retrovnc/src/egl.rs +++ b/crates/retrovnc/src/egl.rs @@ -27,6 +27,29 @@ mod egl_impl { devices: *mut EGLint, ) -> types::EGLBoolean; + /// A helper to get a display on the EGL "Device" platform, which allows headless rendering, + /// without any window system interface. + pub unsafe fn get_device_platform_display() -> types::EGLDisplay { + const NR_DEVICES_MAX: usize = 16; + let mut devices: [types::EGLDeviceEXT; NR_DEVICES_MAX] = [std::ptr::null(); NR_DEVICES_MAX]; + + let mut nr_devices_real: EGLint = 0; + + let query_devices_ext: QueryDevicesExt = + std::mem::transmute(GetProcAddress(b"eglQueryDevicesEXT\0".as_ptr() as *const i8)); + let get_platform_display_ext: GetPlatformDisplayExt = std::mem::transmute(GetProcAddress( + b"eglGetPlatformDisplayEXT\0".as_ptr() as *const i8, + )); + + (query_devices_ext)( + NR_DEVICES_MAX as i32, + devices.as_mut_ptr(), + std::ptr::addr_of_mut!(nr_devices_real), + ); + + (get_platform_display_ext)(PLATFORM_DEVICE_EXT, devices[0], std::ptr::null()) + } + // link EGL as a library dependency #[link(name = "EGL")] extern "C" {} diff --git a/crates/retrovnc/src/main.rs b/crates/retrovnc/src/main.rs index 68a0bbd..1543169 100644 --- a/crates/retrovnc/src/main.rs +++ b/crates/retrovnc/src/main.rs @@ -1,4 +1,4 @@ -use std::{net::Ipv4Addr, path::Path}; +use std::{net::Ipv4Addr, path::Path, time::Duration}; use anyhow::Result; @@ -46,6 +46,10 @@ struct App { hw_render: bool, + // EGL state + egl_display: egl::types::EGLDisplay, + egl_context: egl::types::EGLContext, + // OpenGL object IDs texture_id: gl::types::GLuint, renderbuffer_id: gl::types::GLuint, @@ -62,6 +66,8 @@ impl App { rfb_server: RfbServer::new(rfb_config)?, pad: RetroPad::new(), hw_render: false, + egl_display: std::ptr::null(), + egl_context: std::ptr::null(), texture_id: 0, renderbuffer_id: 0, fbo_id: 0, @@ -120,8 +126,72 @@ impl App { Ok(()) } - fn hw_gl_resize(&mut self, width: u32, height: u32) { + /// Initalizes a headless EGL context for OpenGL rendering. + unsafe fn hw_gl_egl_init(&mut self) { + self.egl_display = egl::get_device_platform_display(); + + self.egl_context = { + const EGL_CONFIG_ATTRIBUTES: [egl::types::EGLenum; 13] = [ + egl::SURFACE_TYPE, + egl::PBUFFER_BIT, + egl::BLUE_SIZE, + 8, + egl::RED_SIZE, + 8, + egl::BLUE_SIZE, + 8, + egl::DEPTH_SIZE, + 8, + egl::RENDERABLE_TYPE, + egl::OPENGL_BIT, + egl::NONE, + ]; + let mut egl_major: egl::EGLint = 0; + let mut egl_minor: egl::EGLint = 0; + + let mut egl_config_count: egl::EGLint = 0; + + let mut config: egl::types::EGLConfig = std::ptr::null(); + + egl::Initialize( + self.egl_display, + std::ptr::addr_of_mut!(egl_major), + std::ptr::addr_of_mut!(egl_minor), + ); + + egl::ChooseConfig( + self.egl_display, + EGL_CONFIG_ATTRIBUTES.as_ptr() as *const egl::EGLint, + std::ptr::addr_of_mut!(config), + 1, + std::ptr::addr_of_mut!(egl_config_count), + ); + + egl::BindAPI(egl::OPENGL_API); + + let context = + egl::CreateContext(self.egl_display, config, egl::NO_CONTEXT, std::ptr::null()); + + // Make the context current on the display so OpenGL routines "just work" + egl::MakeCurrent(self.egl_display, egl::NO_SURFACE, egl::NO_SURFACE, context); + + context + }; + } + + /// Destroys EGL resources. + unsafe fn hw_gl_egl_exit(&mut self) { + // Release the EGL context we created before destroying it + egl::MakeCurrent(self.egl_display, egl::NO_SURFACE, egl::NO_SURFACE, egl::NO_CONTEXT); + egl::DestroyContext(self.egl_display, self.egl_context); + egl::Terminate(self.egl_display); + self.egl_display = std::ptr::null(); + self.egl_context = std::ptr::null(); + } + + fn hw_gl_resize_fbo(&mut self, width: u32, height: u32) { // TODO: cleanup "delete" codepaths so we aren't duplicating as much. + // (we should probably just have a "create" and "delete" FBO pair.) unsafe { if self.fbo_id == 0 { gl::GenFramebuffers(1, std::ptr::addr_of_mut!(self.fbo_id)); @@ -195,6 +265,7 @@ impl App { let id = self.fbo_id; self.get_frontend().set_gl_fbo(id); + // Resize the readback buffer self.readback_buffer.resize((width * height) as usize, 0); } } @@ -204,12 +275,13 @@ impl App { let frontend = self.get_frontend(); let av_info = frontend.get_av_info().expect("???"); - let step_ms = ((1.0 / av_info.timing.fps) * 1000.) as u64; + let step_ms = (1.0 / av_info.timing.fps) * 1000.; + let step_duration = Duration::from_micros((step_ms * 1000.) as u64); // Do the main loop loop { frontend.run_frame(); - std::thread::sleep(std::time::Duration::from_millis(step_ms)); + std::thread::sleep(step_duration); } } } @@ -220,7 +292,7 @@ impl FrontendInterface for App { // Resize OpenGL resources if we need to. if self.hw_render { - self.hw_gl_resize(width, height); + self.hw_gl_resize_fbo(width, height); } self.rfb_server.resize(width as u16, height as u16); @@ -274,19 +346,9 @@ impl FrontendInterface for App { panic!("Cannot initalize HW rendering more than once"); } - let display = unsafe { - const NR_DEVICES_MAX: usize = 16; - let mut devices: [egl::types::EGLDeviceEXT; NR_DEVICES_MAX] = - [std::ptr::null(); NR_DEVICES_MAX]; - - let mut nr_devices_real: egl::EGLint = 0; - - let query_devices_ext: egl::QueryDevicesExt = std::mem::transmute(egl::GetProcAddress( - b"eglQueryDevicesEXT\0".as_ptr() as *const i8, - )); - let get_platform_display_ext: egl::GetPlatformDisplayExt = std::mem::transmute( - egl::GetProcAddress(b"eglGetPlatformDisplayEXT\0".as_ptr() as *const i8), - ); + unsafe { + // Initalize EGL + self.hw_gl_egl_init(); // load OpenGL functions (using EGL loader. We should probably check the one extension exists) gl::load_with(|s| { @@ -294,66 +356,13 @@ impl FrontendInterface for App { std::mem::transmute(egl::GetProcAddress(str.as_ptr())) }); - (query_devices_ext)( - NR_DEVICES_MAX as i32, - devices.as_mut_ptr(), - std::ptr::addr_of_mut!(nr_devices_real), - ); - - (get_platform_display_ext)(egl::PLATFORM_DEVICE_EXT, devices[0], std::ptr::null()) - }; - - let context = unsafe { - const EGL_CONFIG_ATTRIBUTES: [egl::types::EGLenum; 13] = [ - egl::SURFACE_TYPE, - egl::PBUFFER_BIT, - egl::BLUE_SIZE, - 8, - egl::RED_SIZE, - 8, - egl::BLUE_SIZE, - 8, - egl::DEPTH_SIZE, - 8, - egl::RENDERABLE_TYPE, - egl::OPENGL_BIT, - egl::NONE, - ]; - let mut egl_major: egl::EGLint = 0; - let mut egl_minor: egl::EGLint = 0; - - let mut egl_config_count: egl::EGLint = 0; - - let mut config: egl::types::EGLConfig = std::ptr::null(); - - egl::Initialize( - display, - std::ptr::addr_of_mut!(egl_major), - std::ptr::addr_of_mut!(egl_minor), - ); - - egl::ChooseConfig( - display, - EGL_CONFIG_ATTRIBUTES.as_ptr() as *const egl::EGLint, - std::ptr::addr_of_mut!(config), - 1, - std::ptr::addr_of_mut!(egl_config_count), - ); - - egl::BindAPI(egl::OPENGL_API); - - egl::CreateContext(display, config, egl::NO_CONTEXT, std::ptr::null()) - }; - - unsafe { - egl::MakeCurrent(display, egl::NO_SURFACE, egl::NO_SURFACE, context); - + // set OpenGL debug message callback gl::Enable(gl::DEBUG_OUTPUT); gl::DebugMessageCallback(Some(opengl_message_callback), std::ptr::null()); // Resize to initial dimensions (this will create an FBO as well) let dimensions = self.get_frontend().get_size(); - self.hw_gl_resize(dimensions.0, dimensions.1); + self.hw_gl_resize_fbo(dimensions.0, dimensions.1); } self.hw_render = true;