cleanup EGL + OpenGL init into given passes

This commit is contained in:
Lily Tsuru 2024-08-05 22:17:32 -04:00
parent 46520d22ae
commit 897f45e49f
3 changed files with 105 additions and 74 deletions

View file

@ -247,7 +247,6 @@ pub(crate) unsafe extern "C" fn video_refresh_callback(
.resize((pitch * height as usize) as usize, 0); .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 x in 0..pitch as usize {
for y in 0..height as usize { for y in 0..height as usize {
let rgb = Rgb565::from_rgb565(pixel_data_slice[y * pitch as usize + x]); let rgb = Rgb565::from_rgb565(pixel_data_slice[y * pitch as usize + x]);

View file

@ -27,6 +27,29 @@ mod egl_impl {
devices: *mut EGLint, devices: *mut EGLint,
) -> types::EGLBoolean; ) -> 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 EGL as a library dependency
#[link(name = "EGL")] #[link(name = "EGL")]
extern "C" {} extern "C" {}

View file

@ -1,4 +1,4 @@
use std::{net::Ipv4Addr, path::Path}; use std::{net::Ipv4Addr, path::Path, time::Duration};
use anyhow::Result; use anyhow::Result;
@ -46,6 +46,10 @@ struct App {
hw_render: bool, hw_render: bool,
// EGL state
egl_display: egl::types::EGLDisplay,
egl_context: egl::types::EGLContext,
// OpenGL object IDs // OpenGL object IDs
texture_id: gl::types::GLuint, texture_id: gl::types::GLuint,
renderbuffer_id: gl::types::GLuint, renderbuffer_id: gl::types::GLuint,
@ -62,6 +66,8 @@ impl App {
rfb_server: RfbServer::new(rfb_config)?, rfb_server: RfbServer::new(rfb_config)?,
pad: RetroPad::new(), pad: RetroPad::new(),
hw_render: false, hw_render: false,
egl_display: std::ptr::null(),
egl_context: std::ptr::null(),
texture_id: 0, texture_id: 0,
renderbuffer_id: 0, renderbuffer_id: 0,
fbo_id: 0, fbo_id: 0,
@ -120,8 +126,72 @@ impl App {
Ok(()) 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. // TODO: cleanup "delete" codepaths so we aren't duplicating as much.
// (we should probably just have a "create" and "delete" FBO pair.)
unsafe { unsafe {
if self.fbo_id == 0 { if self.fbo_id == 0 {
gl::GenFramebuffers(1, std::ptr::addr_of_mut!(self.fbo_id)); gl::GenFramebuffers(1, std::ptr::addr_of_mut!(self.fbo_id));
@ -195,6 +265,7 @@ impl App {
let id = self.fbo_id; let id = self.fbo_id;
self.get_frontend().set_gl_fbo(id); self.get_frontend().set_gl_fbo(id);
// Resize the readback buffer
self.readback_buffer.resize((width * height) as usize, 0); self.readback_buffer.resize((width * height) as usize, 0);
} }
} }
@ -204,12 +275,13 @@ impl App {
let frontend = self.get_frontend(); let frontend = self.get_frontend();
let av_info = frontend.get_av_info().expect("???"); 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 // Do the main loop
loop { loop {
frontend.run_frame(); 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. // Resize OpenGL resources if we need to.
if self.hw_render { 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); 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"); panic!("Cannot initalize HW rendering more than once");
} }
let display = unsafe { unsafe {
const NR_DEVICES_MAX: usize = 16; // Initalize EGL
let mut devices: [egl::types::EGLDeviceEXT; NR_DEVICES_MAX] = self.hw_gl_egl_init();
[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),
);
// load OpenGL functions (using EGL loader. We should probably check the one extension exists) // load OpenGL functions (using EGL loader. We should probably check the one extension exists)
gl::load_with(|s| { gl::load_with(|s| {
@ -294,66 +356,13 @@ impl FrontendInterface for App {
std::mem::transmute(egl::GetProcAddress(str.as_ptr())) std::mem::transmute(egl::GetProcAddress(str.as_ptr()))
}); });
(query_devices_ext)( // set OpenGL debug message callback
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);
gl::Enable(gl::DEBUG_OUTPUT); gl::Enable(gl::DEBUG_OUTPUT);
gl::DebugMessageCallback(Some(opengl_message_callback), std::ptr::null()); gl::DebugMessageCallback(Some(opengl_message_callback), std::ptr::null());
// Resize to initial dimensions (this will create an FBO as well) // Resize to initial dimensions (this will create an FBO as well)
let dimensions = self.get_frontend().get_size(); 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; self.hw_render = true;