diff --git a/ferretro_base/src/retro/wrapped_types.rs b/ferretro_base/src/retro/wrapped_types.rs index dcdc267..0c31ab0 100644 --- a/ferretro_base/src/retro/wrapped_types.rs +++ b/ferretro_base/src/retro/wrapped_types.rs @@ -33,6 +33,25 @@ impl<'a> VideoFrame<'a> { _ => None, } } + + pub fn dimensions(&self) -> (c_uint, c_uint) { + match self { + VideoFrame::XRGB1555 { width, height, .. } + | VideoFrame::RGB565 { width, height, .. } + | VideoFrame::XRGB8888 { width, height, .. } + | VideoFrame::Duplicate { width, height, .. } + | VideoFrame::HardwareRender { width, height, .. } => (*width, *height), + } + } + + pub fn pixel_format(&self) -> Option { + match self { + VideoFrame::XRGB1555 { .. } => Some(PixelFormat::ARGB1555), + VideoFrame::RGB565 { .. } => Some(PixelFormat::RGB565), + VideoFrame::XRGB8888 { .. } => Some(PixelFormat::ARGB8888), + _ => None, + } + } } #[derive(Clone, Copy, Debug)] diff --git a/ferretro_components/examples/multifunction_emulator.rs b/ferretro_components/examples/multifunction_emulator.rs index 0291802..9c1990a 100644 --- a/ferretro_components/examples/multifunction_emulator.rs +++ b/ferretro_components/examples/multifunction_emulator.rs @@ -32,6 +32,9 @@ struct Opt { /// Recorded video to write. #[structopt(short, long, parse(from_os_str))] video: Option, + /// Disable OpenGL context creation. + #[structopt(long)] + no_opengl: bool, } pub fn main() -> Result<(), Box> { @@ -54,8 +57,20 @@ pub fn main() -> Result<(), Box> { emu.register_component(ffmpeg_comp)?; } - let sdl2_ogl = SimpleSdl2OpenglComponent::new(&mut sdl_context, emu.libretro_core())?; - emu.register_component(sdl2_ogl)?; + let sdl2_ogl_res = if opt.no_opengl { + Err("OpenGL disabled".into()) + } else { + SimpleSdl2OpenglComponent::new(&mut sdl_context, emu.libretro_core()) + }; + + if let Ok(sdl2_ogl) = sdl2_ogl_res { + emu.register_component(sdl2_ogl)?; + } else { + match SimpleSdl2CanvasComponent::new(&mut sdl_context, emu.libretro_core()) { + Ok(sdl2_canvas) => emu.register_component(sdl2_canvas)?, + Err(e) => eprintln!("Couldn't initialize SDL2 video component: {:?}", e), + } + } emu.register_component(SimpleSdl2AudioComponent::new(&mut sdl_context)?)?; diff --git a/ferretro_components/src/provided/sdl2/canvas.rs b/ferretro_components/src/provided/sdl2/canvas.rs index 0ea222b..20522a3 100644 --- a/ferretro_components/src/provided/sdl2/canvas.rs +++ b/ferretro_components/src/provided/sdl2/canvas.rs @@ -7,13 +7,42 @@ use sdl2::Sdl; use sdl2::rect::Rect; use sdl2::render::WindowCanvas; +pub(crate) fn paint_frame_on_canvas(frame: &VideoFrame, canvas: &mut WindowCanvas) -> bool { + if let Some((pixel_data, pitch)) = frame.data_pitch_as_bytes() { + let (width, height) = frame.dimensions(); + let pixel_format = frame.pixel_format().map(sdl2_pixfmt); + + if let Ok(mut tex) = canvas + .texture_creator() + .create_texture_static(pixel_format, width, height) + { + let rect = Rect::new(0, 0, width, height); + if tex.update(rect, pixel_data, pitch).is_ok() { + canvas.clear(); + canvas.copy(&tex, None, None).unwrap(); + } + } + canvas.present(); + true + } else { + false + } +} + +pub(crate) fn sdl2_pixfmt(retro_fmt: PixelFormat) -> sdl2::pixels::PixelFormatEnum { + match retro_fmt { + PixelFormat::ARGB1555 => sdl2::pixels::PixelFormatEnum::ARGB1555, + PixelFormat::ARGB8888 => sdl2::pixels::PixelFormatEnum::ARGB8888, + PixelFormat::RGB565 => sdl2::pixels::PixelFormatEnum::RGB565, + } +} + /// Creates a root window in SDL2, then displays each 2D video frame provided by the core /// by converting it to a [sdl2::render::Texture] and copying it to the window's canvas. /// /// This component has no public interface and manages the SDL2 window on its own. pub struct SimpleSdl2CanvasComponent { canvas: WindowCanvas, - pixel_format: sdl2::pixels::PixelFormatEnum, } impl SimpleSdl2CanvasComponent { @@ -24,8 +53,6 @@ impl SimpleSdl2CanvasComponent { unsafe { CStr::from_ptr(sys_info.library_name) }.to_string_lossy() ); - let pixel_format = sdl2::pixels::PixelFormatEnum::ARGB1555; - let window = sdl_context .video()? .window(title.as_str(), 256, 224) @@ -35,7 +62,6 @@ impl SimpleSdl2CanvasComponent { Ok(SimpleSdl2CanvasComponent { canvas, - pixel_format, }) } } @@ -49,34 +75,10 @@ impl RetroComponent for SimpleSdl2CanvasComponent { impl RetroCallbacks for SimpleSdl2CanvasComponent { fn video_refresh(&mut self, frame: &VideoFrame) { - match frame { - VideoFrame::XRGB1555 { width, height, .. } - | VideoFrame::RGB565 { width, height, .. } - | VideoFrame::XRGB8888 { width, height, .. } => { - let rect = Rect::new(0, 0, *width, *height); - - if let Ok(mut tex) = self.canvas - .texture_creator() - .create_texture_static(self.pixel_format, *width, *height) - { - let (pixel_data, pitch) = frame.data_pitch_as_bytes().unwrap(); - if tex.update(rect, pixel_data, pitch).is_ok() { - self.canvas.clear(); - self.canvas.copy(&tex, None, None).unwrap(); - } - } - self.canvas.present(); - } - _ => {} - } + paint_frame_on_canvas(frame, &mut self.canvas); } - fn set_pixel_format(&mut self, pix_fmt: PixelFormat) -> Option { - self.pixel_format = match pix_fmt { - PixelFormat::ARGB1555 => sdl2::pixels::PixelFormatEnum::RGB555, - PixelFormat::ARGB8888 => sdl2::pixels::PixelFormatEnum::ARGB8888, - PixelFormat::RGB565 => sdl2::pixels::PixelFormatEnum::RGB565, - }; + fn set_pixel_format(&mut self, _pix_fmt: PixelFormat) -> Option { Some(true) } diff --git a/ferretro_components/src/provided/sdl2/fps.rs b/ferretro_components/src/provided/sdl2/fps.rs index ff5f91a..ecc05cd 100644 --- a/ferretro_components/src/provided/sdl2/fps.rs +++ b/ferretro_components/src/provided/sdl2/fps.rs @@ -9,6 +9,16 @@ pub struct SimpleSdl2FramerateLimitComponent { fps_manager: FPSManager, } +impl Default for SimpleSdl2FramerateLimitComponent { + fn default() -> Self { + SimpleSdl2FramerateLimitComponent { + did_sleep: false, + started_video: false, + fps_manager: FPSManager::new(), + } + } +} + impl RetroComponent for SimpleSdl2FramerateLimitComponent { fn pre_run(&mut self, _retro: &mut LibretroWrapper) -> ControlFlow { self.did_sleep = false; diff --git a/ferretro_components/src/provided/sdl2/opengl.rs b/ferretro_components/src/provided/sdl2/opengl.rs index 04113c8..c18b344 100644 --- a/ferretro_components/src/provided/sdl2/opengl.rs +++ b/ferretro_components/src/provided/sdl2/opengl.rs @@ -5,9 +5,10 @@ use std::os::raw::c_uint; use std::path::Path; use sdl2::Sdl; -use sdl2::rect::Rect; use sdl2::render::WindowCanvas; +use super::canvas::paint_frame_on_canvas; + /// Create a root window in SDL2 with an OpenGL context and attaches libretro's /// `hw_get_proc_address` calls to that of the [sdl2::VideoSubsystem]. /// @@ -20,7 +21,6 @@ use sdl2::render::WindowCanvas; pub struct SimpleSdl2OpenglComponent { canvas: WindowCanvas, window_fbo: c_uint, - pixel_format: sdl2::pixels::PixelFormatEnum, hw_context_reset_fn: Option, hw_context_destroy_fn: Option, glGetIntegerv: unsafe extern "C" fn(c_uint, *mut c_uint), @@ -35,8 +35,6 @@ impl SimpleSdl2OpenglComponent { unsafe { CStr::from_ptr(sys_info.library_name) }.to_string_lossy() ); - let pixel_format = sdl2::pixels::PixelFormatEnum::ARGB1555; - let video = sdl_context.video()?; // default to old libsnes 256x224 window size until load_game (prereq of get_system_av_info) let window = video @@ -65,7 +63,6 @@ impl SimpleSdl2OpenglComponent { Ok(SimpleSdl2OpenglComponent { canvas, window_fbo, - pixel_format, hw_context_reset_fn: None, hw_context_destroy_fn: None, glGetIntegerv, @@ -101,36 +98,16 @@ impl RetroComponent for SimpleSdl2OpenglComponent { impl RetroCallbacks for SimpleSdl2OpenglComponent { fn video_refresh(&mut self, frame: &VideoFrame) { match frame { - VideoFrame::XRGB1555 { width, height, .. } - | VideoFrame::RGB565 { width, height, .. } - | VideoFrame::XRGB8888 { width, height, .. } => { - let rect = Rect::new(0, 0, *width, *height); - if let Ok(mut tex) = self.canvas - .texture_creator() - .create_texture_static(self.pixel_format, *width, *height) - { - let (pixel_data, pitch) = frame.data_pitch_as_bytes().unwrap(); - if tex.update(rect, pixel_data, pitch).is_ok() { - self.canvas.clear(); - self.canvas.copy(&tex, None, None).unwrap(); - } - } - self.canvas.present(); - } VideoFrame::HardwareRender { .. } => { self.canvas.present(); unsafe { (self.glClear)(gl::COLOR_BUFFER_BIT); } } VideoFrame::Duplicate { .. } => {} + _ => { paint_frame_on_canvas(frame, &mut self.canvas); }, } } - fn set_pixel_format(&mut self, pix_fmt: PixelFormat) -> Option { - self.pixel_format = match pix_fmt { - PixelFormat::ARGB1555 => sdl2::pixels::PixelFormatEnum::RGB555, - PixelFormat::ARGB8888 => sdl2::pixels::PixelFormatEnum::ARGB8888, - PixelFormat::RGB565 => sdl2::pixels::PixelFormatEnum::RGB565, - }; + fn set_pixel_format(&mut self, _pix_fmt: PixelFormat) -> Option { Some(true) }