Support 2D video_refresh in the SDL+GL component
...and alter the behavior of ThreadSleep component a bit.
This commit is contained in:
parent
0c5d0b5338
commit
71240de38c
|
@ -43,9 +43,8 @@ pub fn main() {
|
||||||
|
|
||||||
emu.register_component(StderrLogComponent { prefix: "{log} ".to_string() });
|
emu.register_component(StderrLogComponent { prefix: "{log} ".to_string() });
|
||||||
|
|
||||||
//let sdl2_canvas = SimpleSdl2CanvasComponent::new(&mut sdl_context, emu.libretro_core());
|
let sdl2_ogl = SimpleSdl2OpenglComponent::new(&mut sdl_context, emu.libretro_core()).unwrap();
|
||||||
let sdl2_canvas = SimpleSdl2OpenglComponent::new(&mut sdl_context, emu.libretro_core()).unwrap();
|
emu.register_component(sdl2_ogl);
|
||||||
emu.register_component(sdl2_canvas);
|
|
||||||
|
|
||||||
let sdl2_audio = SimpleSdl2AudioComponent::new(&mut sdl_context, emu.libretro_core());
|
let sdl2_audio = SimpleSdl2AudioComponent::new(&mut sdl_context, emu.libretro_core());
|
||||||
emu.register_component(sdl2_audio);
|
emu.register_component(sdl2_audio);
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::base::ControlFlow;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
@ -40,20 +39,21 @@ impl SimpleSdl2CanvasComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RetroComponent for SimpleSdl2CanvasComponent {}
|
||||||
impl RetroCallbacks for SimpleSdl2CanvasComponent {
|
impl RetroCallbacks for SimpleSdl2CanvasComponent {
|
||||||
fn video_refresh(&mut self, data: &[u8], width: u32, height: u32, pitch: u32) {
|
fn video_refresh(&mut self, data: &[u8], width: u32, height: u32, pitch: u32) {
|
||||||
let rect = Rect::new(0, 0, width, height);
|
let rect = Rect::new(0, 0, width, height);
|
||||||
|
|
||||||
if let Ok(mut tex) =
|
if let Ok(mut tex) = self.canvas
|
||||||
self.canvas
|
.texture_creator()
|
||||||
.texture_creator()
|
.create_texture_static(self.pixel_format, width, height)
|
||||||
.create_texture_static(self.pixel_format, width, height)
|
|
||||||
{
|
{
|
||||||
if tex.update(rect, data, pitch as usize).is_ok() {
|
if tex.update(rect, data, pitch as usize).is_ok() {
|
||||||
self.canvas.clear();
|
self.canvas.clear();
|
||||||
self.canvas.copy(&tex, None, None).unwrap();
|
self.canvas.copy(&tex, None, None).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.canvas.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_pixel_format(&mut self, pix_fmt: PixelFormat) -> Option<bool> {
|
fn set_pixel_format(&mut self, pix_fmt: PixelFormat) -> Option<bool> {
|
||||||
|
@ -83,10 +83,3 @@ impl RetroCallbacks for SimpleSdl2CanvasComponent {
|
||||||
Some(true)
|
Some(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RetroComponent for SimpleSdl2CanvasComponent {
|
|
||||||
fn post_run(&mut self, _retro: &mut LibretroWrapper) -> ControlFlow {
|
|
||||||
self.canvas.present();
|
|
||||||
ControlFlow::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::base::ControlFlow;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::os::raw::c_uint;
|
use std::os::raw::c_uint;
|
||||||
|
|
||||||
use sdl2::Sdl;
|
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
|
// 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;
|
const GL_FRAMEBUFFER_BINDING: c_uint = 0x8CA6;
|
||||||
|
@ -13,9 +13,8 @@ const GL_COLOR_BUFFER_BIT: c_uint = 0x00004000;
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub struct SimpleSdl2OpenglComponent {
|
pub struct SimpleSdl2OpenglComponent {
|
||||||
window: Window,
|
canvas: WindowCanvas,
|
||||||
window_fbo: c_uint,
|
window_fbo: c_uint,
|
||||||
gl_context: GLContext,
|
|
||||||
pixel_format: sdl2::pixels::PixelFormatEnum,
|
pixel_format: sdl2::pixels::PixelFormatEnum,
|
||||||
hw_context_reset_fn: Option<HwContextResetFn>,
|
hw_context_reset_fn: Option<HwContextResetFn>,
|
||||||
hw_context_destroy_fn: Option<HwContextResetFn>,
|
hw_context_destroy_fn: Option<HwContextResetFn>,
|
||||||
|
@ -49,15 +48,18 @@ impl SimpleSdl2OpenglComponent {
|
||||||
std::mem::transmute(video.gl_get_proc_address("glClear"))
|
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
|
// 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
|
// likely to remain `0` on any platform that isn't iOS, but we'll do it anyhow.
|
||||||
let gl_context = window.gl_create_context()?;
|
// 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 };
|
let window_fbo = unsafe { let mut fbo = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mut fbo); fbo };
|
||||||
|
|
||||||
Ok(SimpleSdl2OpenglComponent {
|
Ok(SimpleSdl2OpenglComponent {
|
||||||
window,
|
canvas,
|
||||||
window_fbo,
|
window_fbo,
|
||||||
gl_context,
|
|
||||||
pixel_format,
|
pixel_format,
|
||||||
hw_context_reset_fn: None,
|
hw_context_reset_fn: None,
|
||||||
hw_context_destroy_fn: None,
|
hw_context_destroy_fn: None,
|
||||||
|
@ -72,7 +74,6 @@ impl SimpleSdl2OpenglComponent {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
(self.glGetIntegerv)(GL_FRAMEBUFFER_BINDING, &mut fbo);
|
(self.glGetIntegerv)(GL_FRAMEBUFFER_BINDING, &mut fbo);
|
||||||
}
|
}
|
||||||
eprintln!("get_framebuffer_binding: {}", fbo);
|
|
||||||
fbo
|
fbo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,8 +86,25 @@ impl SimpleSdl2OpenglComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RetroComponent for SimpleSdl2OpenglComponent {}
|
||||||
impl RetroCallbacks 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) {
|
fn video_refresh_hw(&mut self, _width: c_uint, _height: c_uint) {
|
||||||
|
self.canvas.present();
|
||||||
unsafe { (self.glClear)(GL_COLOR_BUFFER_BIT); }
|
unsafe { (self.glClear)(GL_COLOR_BUFFER_BIT); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,12 +117,11 @@ impl RetroCallbacks for SimpleSdl2OpenglComponent {
|
||||||
Some(true)
|
Some(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: depth, stencil, cache_context, bottom_left_origin?
|
||||||
fn set_hw_render(&mut self, hw_render_callback: &HwRenderCallback) -> Option<bool> {
|
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_reset_fn.replace(hw_render_callback.context_reset);
|
||||||
self.hw_context_destroy_fn.replace(hw_render_callback.context_destroy);
|
self.hw_context_destroy_fn.replace(hw_render_callback.context_destroy);
|
||||||
|
|
||||||
self.call_context_reset();
|
self.call_context_reset();
|
||||||
|
|
||||||
Some(true)
|
Some(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,11 +138,29 @@ impl RetroCallbacks for SimpleSdl2OpenglComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_geometry(&mut self, geom: &GameGeometry) -> Option<bool> {
|
fn set_geometry(&mut self, geom: &GameGeometry) -> Option<bool> {
|
||||||
self.window.set_size(geom.base_width, geom.base_height).ok()?;
|
let _ = self.canvas.window_mut().set_size(geom.base_width, geom.base_height);
|
||||||
self.call_context_destroy()?;
|
let _ = self.canvas.set_logical_size(geom.base_width, geom.base_height);
|
||||||
self.gl_context = self.window.gl_create_context().ok()?;
|
|
||||||
self.window_fbo = self.current_framebuffer_binding();
|
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()?;
|
self.call_context_reset()?;
|
||||||
|
*/
|
||||||
Some(true)
|
Some(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,13 +169,12 @@ impl RetroCallbacks for SimpleSdl2OpenglComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hw_get_proc_address(&mut self, sym: &str) -> Option<*const ()> {
|
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 {
|
impl Drop for SimpleSdl2OpenglComponent {
|
||||||
fn post_run(&mut self, _retro: &mut LibretroWrapper) -> ControlFlow {
|
fn drop(&mut self) {
|
||||||
self.window.gl_swap_window();
|
self.call_context_destroy();
|
||||||
ControlFlow::Continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::os::raw::c_uint;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
@ -5,8 +6,6 @@ use crate::base::ControlFlow;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use ferretro_base::retro::ffi::{Message, Language};
|
use ferretro_base::retro::ffi::{Message, Language};
|
||||||
|
|
||||||
use std::os::raw::c_uint;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct PathBufComponent {
|
pub struct PathBufComponent {
|
||||||
pub sys_path: Option<PathBuf>,
|
pub sys_path: Option<PathBuf>,
|
||||||
|
@ -210,23 +209,49 @@ impl RetroCallbacks for StderrCallTraceComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SleepFramerateLimitComponent {
|
pub struct SleepFramerateLimitComponent {
|
||||||
|
did_sleep: bool,
|
||||||
fps: f64,
|
fps: f64,
|
||||||
frame_begin: Instant,
|
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 {
|
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> {
|
fn set_system_av_info(&mut self, system_av_info: &SystemAvInfo) -> Option<bool> {
|
||||||
self.fps = system_av_info.timing.fps;
|
self.fps = system_av_info.timing.fps;
|
||||||
Some(true)
|
Some(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RetroComponent for SleepFramerateLimitComponent {
|
impl SleepFramerateLimitComponent {
|
||||||
fn pre_run(&mut self, _retro: &mut LibretroWrapper) -> ControlFlow {
|
pub fn new(retro: &mut LibretroWrapper) -> Self {
|
||||||
self.frame_begin = Instant::now();
|
SleepFramerateLimitComponent {
|
||||||
ControlFlow::Continue
|
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.
|
// similar hack to the sample rate, make sure we don't divide by zero.
|
||||||
let mut spf = 1.0 / self.fps;
|
let mut spf = 1.0 / self.fps;
|
||||||
if spf.is_nan() || spf.is_infinite() {
|
if spf.is_nan() || spf.is_infinite() {
|
||||||
|
@ -235,15 +260,8 @@ impl RetroComponent for SleepFramerateLimitComponent {
|
||||||
Duration::from_secs_f64(spf)
|
Duration::from_secs_f64(spf)
|
||||||
.checked_sub(self.frame_begin.elapsed())
|
.checked_sub(self.frame_begin.elapsed())
|
||||||
.map(std::thread::sleep);
|
.map(std::thread::sleep);
|
||||||
ControlFlow::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SleepFramerateLimitComponent {
|
self.did_sleep = true;
|
||||||
pub fn new(retro: &mut LibretroWrapper) -> Self {
|
self.frame_begin = Instant::now();
|
||||||
SleepFramerateLimitComponent {
|
|
||||||
fps: retro.get_system_av_info().timing.fps,
|
|
||||||
frame_begin: Instant::now(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue