Support 2D video_refresh in the SDL+GL component

...and alter the behavior of ThreadSleep component a bit.
This commit is contained in:
lifning 2021-08-21 20:31:26 -07:00
parent 0c5d0b5338
commit 71240de38c
4 changed files with 94 additions and 50 deletions

View File

@ -43,9 +43,8 @@ pub fn main() {
emu.register_component(StderrLogComponent { prefix: "{log} ".to_string() });
//let sdl2_canvas = SimpleSdl2CanvasComponent::new(&mut sdl_context, emu.libretro_core());
let sdl2_canvas = SimpleSdl2OpenglComponent::new(&mut sdl_context, emu.libretro_core()).unwrap();
emu.register_component(sdl2_canvas);
let sdl2_ogl = SimpleSdl2OpenglComponent::new(&mut sdl_context, emu.libretro_core()).unwrap();
emu.register_component(sdl2_ogl);
let sdl2_audio = SimpleSdl2AudioComponent::new(&mut sdl_context, emu.libretro_core());
emu.register_component(sdl2_audio);

View File

@ -1,4 +1,3 @@
use crate::base::ControlFlow;
use crate::prelude::*;
use std::ffi::CStr;
@ -40,20 +39,21 @@ impl SimpleSdl2CanvasComponent {
}
}
impl RetroComponent for SimpleSdl2CanvasComponent {}
impl RetroCallbacks for SimpleSdl2CanvasComponent {
fn video_refresh(&mut self, data: &[u8], width: u32, height: u32, pitch: u32) {
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)
if let Ok(mut tex) = self.canvas
.texture_creator()
.create_texture_static(self.pixel_format, width, height)
{
if tex.update(rect, data, pitch as usize).is_ok() {
self.canvas.clear();
self.canvas.copy(&tex, None, None).unwrap();
}
}
self.canvas.present();
}
fn set_pixel_format(&mut self, pix_fmt: PixelFormat) -> Option<bool> {
@ -83,10 +83,3 @@ impl RetroCallbacks for SimpleSdl2CanvasComponent {
Some(true)
}
}
impl RetroComponent for SimpleSdl2CanvasComponent {
fn post_run(&mut self, _retro: &mut LibretroWrapper) -> ControlFlow {
self.canvas.present();
ControlFlow::Continue
}
}

View File

@ -1,11 +1,11 @@
use crate::base::ControlFlow;
use crate::prelude::*;
use std::ffi::CStr;
use std::os::raw::c_uint;
use sdl2::Sdl;
use sdl2::video::{GLContext, Window};
use sdl2::rect::Rect;
use sdl2::render::WindowCanvas;
// TODO: get these from somewhere better without pulling in too much of a mess for the sdl2 feature
const GL_FRAMEBUFFER_BINDING: c_uint = 0x8CA6;
@ -13,9 +13,8 @@ const GL_COLOR_BUFFER_BIT: c_uint = 0x00004000;
#[allow(non_snake_case)]
pub struct SimpleSdl2OpenglComponent {
window: Window,
canvas: WindowCanvas,
window_fbo: c_uint,
gl_context: GLContext,
pixel_format: sdl2::pixels::PixelFormatEnum,
hw_context_reset_fn: Option<HwContextResetFn>,
hw_context_destroy_fn: Option<HwContextResetFn>,
@ -49,15 +48,18 @@ impl SimpleSdl2OpenglComponent {
std::mem::transmute(video.gl_get_proc_address("glClear"))
};
let canvas = window.into_canvas()
.accelerated()
.target_texture()
.build()?;
// http://forums.libsdl.org/viewtopic.php?p=43353
// likely to remain `0` on any platform that isn't iOS, but we'll do it anyhow
let gl_context = window.gl_create_context()?;
// likely to remain `0` on any platform that isn't iOS, but we'll do it anyhow.
// SDL_CreateRenderer, called by CanvasBuilder::build, creates a new GL Context.
let window_fbo = unsafe { let mut fbo = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mut fbo); fbo };
Ok(SimpleSdl2OpenglComponent {
window,
canvas,
window_fbo,
gl_context,
pixel_format,
hw_context_reset_fn: None,
hw_context_destroy_fn: None,
@ -72,7 +74,6 @@ impl SimpleSdl2OpenglComponent {
#[allow(non_snake_case)]
(self.glGetIntegerv)(GL_FRAMEBUFFER_BINDING, &mut fbo);
}
eprintln!("get_framebuffer_binding: {}", fbo);
fbo
}
@ -85,8 +86,25 @@ impl SimpleSdl2OpenglComponent {
}
}
impl RetroComponent for SimpleSdl2OpenglComponent {}
impl RetroCallbacks for SimpleSdl2OpenglComponent {
fn video_refresh(&mut self, data: &[u8], width: u32, height: u32, pitch: u32) {
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)
{
if tex.update(rect, data, pitch as usize).is_ok() {
self.canvas.clear();
self.canvas.copy(&tex, None, None).unwrap();
}
}
self.canvas.present();
}
fn video_refresh_hw(&mut self, _width: c_uint, _height: c_uint) {
self.canvas.present();
unsafe { (self.glClear)(GL_COLOR_BUFFER_BIT); }
}
@ -99,12 +117,11 @@ impl RetroCallbacks for SimpleSdl2OpenglComponent {
Some(true)
}
// TODO: depth, stencil, cache_context, bottom_left_origin?
fn set_hw_render(&mut self, hw_render_callback: &HwRenderCallback) -> Option<bool> {
self.hw_context_reset_fn.replace(hw_render_callback.context_reset);
self.hw_context_destroy_fn.replace(hw_render_callback.context_destroy);
self.call_context_reset();
Some(true)
}
@ -121,11 +138,29 @@ impl RetroCallbacks for SimpleSdl2OpenglComponent {
}
fn set_geometry(&mut self, geom: &GameGeometry) -> Option<bool> {
self.window.set_size(geom.base_width, geom.base_height).ok()?;
self.call_context_destroy()?;
self.gl_context = self.window.gl_create_context().ok()?;
let _ = self.canvas.window_mut().set_size(geom.base_width, geom.base_height);
let _ = self.canvas.set_logical_size(geom.base_width, geom.base_height);
self.window_fbo = self.current_framebuffer_binding();
/*
let mut old_canvas = unsafe { MaybeUninit::uninit().assume_init() };
std::mem::swap(&mut old_canvas, &mut self.canvas);
// no funny ? operators here because we've got uninitialized memory in self.canvas!
let mut window = old_canvas.into_window();
window.set_size(geom.base_width, geom.base_height).unwrap();
self.call_context_destroy().unwrap();
let mut new_canvas = window.into_canvas()
.accelerated()
.target_texture()
.build()
.unwrap();
self.window_fbo = self.current_framebuffer_binding();
std::mem::swap(&mut self.canvas, &mut new_canvas);
std::mem::forget(new_canvas);
self.call_context_reset()?;
*/
Some(true)
}
@ -134,13 +169,12 @@ impl RetroCallbacks for SimpleSdl2OpenglComponent {
}
fn hw_get_proc_address(&mut self, sym: &str) -> Option<*const ()> {
Some(self.window.subsystem().gl_get_proc_address(sym))
Some(self.canvas.window().subsystem().gl_get_proc_address(sym))
}
}
impl RetroComponent for SimpleSdl2OpenglComponent {
fn post_run(&mut self, _retro: &mut LibretroWrapper) -> ControlFlow {
self.window.gl_swap_window();
ControlFlow::Continue
impl Drop for SimpleSdl2OpenglComponent {
fn drop(&mut self) {
self.call_context_destroy();
}
}

View File

@ -1,3 +1,4 @@
use std::os::raw::c_uint;
use std::path::PathBuf;
use std::time::{Duration, Instant};
@ -5,8 +6,6 @@ use crate::base::ControlFlow;
use crate::prelude::*;
use ferretro_base::retro::ffi::{Message, Language};
use std::os::raw::c_uint;
#[derive(Default)]
pub struct PathBufComponent {
pub sys_path: Option<PathBuf>,
@ -210,23 +209,49 @@ impl RetroCallbacks for StderrCallTraceComponent {
}
pub struct SleepFramerateLimitComponent {
did_sleep: bool,
fps: f64,
frame_begin: Instant,
}
impl RetroComponent for SleepFramerateLimitComponent {
fn pre_run(&mut self, _retro: &mut LibretroWrapper) -> ControlFlow {
self.did_sleep = false;
ControlFlow::Continue
}
fn post_run(&mut self, _retro: &mut LibretroWrapper) -> ControlFlow {
if !self.did_sleep {
self.do_sleep();
}
ControlFlow::Continue
}
}
impl RetroCallbacks for SleepFramerateLimitComponent {
fn video_refresh(&mut self, _data: &[u8], _width: c_uint, _height: c_uint, _pitch: c_uint) {
self.do_sleep();
}
fn video_refresh_dupe(&mut self, _width: c_uint, _height: c_uint, _pitch: c_uint) {
self.do_sleep();
}
fn video_refresh_hw(&mut self, _width: c_uint, _height: c_uint) {
self.do_sleep();
}
fn set_system_av_info(&mut self, system_av_info: &SystemAvInfo) -> Option<bool> {
self.fps = system_av_info.timing.fps;
Some(true)
}
}
impl RetroComponent for SleepFramerateLimitComponent {
fn pre_run(&mut self, _retro: &mut LibretroWrapper) -> ControlFlow {
self.frame_begin = Instant::now();
ControlFlow::Continue
impl SleepFramerateLimitComponent {
pub fn new(retro: &mut LibretroWrapper) -> Self {
SleepFramerateLimitComponent {
did_sleep: false,
fps: retro.get_system_av_info().timing.fps,
frame_begin: Instant::now(),
}
}
fn post_run(&mut self, _retro: &mut LibretroWrapper) -> ControlFlow {
fn do_sleep(&mut self) {
// similar hack to the sample rate, make sure we don't divide by zero.
let mut spf = 1.0 / self.fps;
if spf.is_nan() || spf.is_infinite() {
@ -235,15 +260,8 @@ impl RetroComponent for SleepFramerateLimitComponent {
Duration::from_secs_f64(spf)
.checked_sub(self.frame_begin.elapsed())
.map(std::thread::sleep);
ControlFlow::Continue
}
}
impl SleepFramerateLimitComponent {
pub fn new(retro: &mut LibretroWrapper) -> Self {
SleepFramerateLimitComponent {
fps: retro.get_system_av_info().timing.fps,
frame_begin: Instant::now(),
}
self.did_sleep = true;
self.frame_begin = Instant::now();
}
}