add some QOL stuff to the SDL2 Simple video components

This commit is contained in:
lifning 2021-11-04 15:27:13 -07:00
parent e95e13df1e
commit 56e070a30d
5 changed files with 82 additions and 59 deletions

View File

@ -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<PixelFormat> {
match self {
VideoFrame::XRGB1555 { .. } => Some(PixelFormat::ARGB1555),
VideoFrame::RGB565 { .. } => Some(PixelFormat::RGB565),
VideoFrame::XRGB8888 { .. } => Some(PixelFormat::ARGB8888),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug)]

View File

@ -32,6 +32,9 @@ struct Opt {
/// Recorded video to write.
#[structopt(short, long, parse(from_os_str))]
video: Option<PathBuf>,
/// Disable OpenGL context creation.
#[structopt(long)]
no_opengl: bool,
}
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
@ -54,8 +57,20 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
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)?)?;

View File

@ -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<bool> {
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<bool> {
Some(true)
}

View File

@ -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;

View File

@ -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<HwContextResetFn>,
hw_context_destroy_fn: Option<HwContextResetFn>,
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<bool> {
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<bool> {
Some(true)
}