From 013cc247b9f8f68571a674c932028864b675183c Mon Sep 17 00:00:00 2001 From: lifning <> Date: Thu, 19 Aug 2021 02:42:21 -0700 Subject: [PATCH 1/6] wip messing around with SDL_GL as a possible hw render provider to later mix in with the existing 2D codepaths --- ferretro_base/src/lib.rs | 2 +- ferretro_base/src/retro/constants.rs | 2 + ferretro_base/src/retro/wrapper.rs | 77 ++++++++----- .../examples/multifunction_emulator.rs | 4 +- ferretro_components/src/base/mod.rs | 63 ++++++++--- ferretro_components/src/lib.rs | 2 +- ferretro_components/src/provided/sdl2.rs | 2 + .../src/provided/sdl2/opengl.rs | 106 ++++++++++++++++++ 8 files changed, 209 insertions(+), 49 deletions(-) create mode 100644 ferretro_components/src/provided/sdl2/opengl.rs diff --git a/ferretro_base/src/lib.rs b/ferretro_base/src/lib.rs index a4c6d02..c280b88 100644 --- a/ferretro_base/src/lib.rs +++ b/ferretro_base/src/lib.rs @@ -6,5 +6,5 @@ pub mod prelude { pub use crate::retro::constants::*; pub use crate::retro::wrapped_types::*; pub use crate::retro::wrapper::{RetroCallbacks, LibretroWrapper, LibretroWrapperAccess}; - pub use crate::retro::ffi::{PixelFormat, GameGeometry, SystemAvInfo, SystemInfo}; + pub use crate::retro::ffi::{PixelFormat, GameGeometry, HwContextResetFn, HwRenderCallback, SystemAvInfo, SystemInfo}; } diff --git a/ferretro_base/src/retro/constants.rs b/ferretro_base/src/retro/constants.rs index faa5252..0b44af3 100644 --- a/ferretro_base/src/retro/constants.rs +++ b/ferretro_base/src/retro/constants.rs @@ -152,5 +152,7 @@ pub enum EnvCmd { SetGeometry = ENVIRONMENT_SET_GEOMETRY, GetUsername = ENVIRONMENT_GET_USERNAME, GetLanguage = ENVIRONMENT_GET_LANGUAGE, + GetCurrentSoftwareFramebuffer = ENVIRONMENT_GET_CURRENT_SOFTWARE_FRAMEBUFFER, + GetHwRenderInterface = ENVIRONMENT_GET_HW_RENDER_INTERFACE, // SetSerializationQuirks = ENVIRONMENT_SET_SERIALIZATION_QUIRKS, } diff --git a/ferretro_base/src/retro/wrapper.rs b/ferretro_base/src/retro/wrapper.rs index 46a9d19..7920677 100644 --- a/ferretro_base/src/retro/wrapper.rs +++ b/ferretro_base/src/retro/wrapper.rs @@ -45,8 +45,10 @@ pub trait RetroCallbacks: Unpin + 'static { /// Certain graphic APIs, such as OpenGL ES, do not like textures /// that are not packed in memory. fn video_refresh(&mut self, data: &[u8], width: c_uint, height: c_uint, pitch: c_uint) {} - /// Called instead of video_refresh when the core reports a duplicate frame (NULL). + /// Called instead of video_refresh when a core reports a duplicate frame (NULL). fn video_refresh_dupe(&mut self, width: c_uint, height: c_uint, pitch: c_uint) {} + /// Called instead of video_refresh when a core uses hardware rendering (HW_FRAMEBUFFER_VALID). + fn video_refresh_hw(&mut self, width: c_uint, height: c_uint) {} /// Renders a single audio frame. Should only be used if implementation /// generates a single sample at a time. /// Format is signed 16-bit native endian. @@ -342,28 +344,33 @@ pub trait RetroCallbacks: Unpin + 'static { fn set_rumble_state(&mut self, port: c_uint, effect: RumbleEffect, strength: u16) -> bool { false } /// Returns current time in microseconds. /// Tries to use the most accurate timer available. - fn perf_get_time_usec_cb(&mut self) -> Time { 0 } + fn perf_get_time_usec(&mut self) -> Time { 0 } /// A simple counter. Usually nanoseconds, but can also be CPU cycles. /// Can be used directly if desired (when creating a more sophisticated /// performance counter system). - fn perf_get_counter_cb(&mut self) -> PerfTick { 0 } + fn perf_get_counter(&mut self) -> PerfTick { 0 } /// Returns a bit-mask of detected CPU features ([libretro_sys]::SIMD_*). - fn perf_get_cpu_features_cb(&mut self) -> u64 { 0 } + fn perf_get_cpu_features(&mut self) -> u64 { 0 } /// Asks frontend to log and/or display the state of performance counters. /// Performance counters can always be poked into manually as well. - fn perf_log_cb(&mut self) {} + fn perf_log(&mut self) {} /// Register a performance counter. /// ident field must be set with a discrete value and other values in /// retro_perf_counter must be 0. /// Registering can be called multiple times. To avoid calling to /// frontend redundantly, you can check registered field first. - fn perf_register_cb(&mut self, counter: &mut PerfCounter) {} + fn perf_register(&mut self, counter: &mut PerfCounter) {} /// Starts a registered counter. - fn perf_start_cb(&mut self, counter: &mut PerfCounter) {} + fn perf_start(&mut self, counter: &mut PerfCounter) {} /// Stops a registered counter. - fn perf_stop_cb(&mut self, counter: &mut PerfCounter) {} + fn perf_stop(&mut self, counter: &mut PerfCounter) {} fn set_sensor_state(&mut self, port: c_uint, action: SensorAction, rate: c_uint) -> bool { false } fn get_sensor_input(&mut self, port: c_uint, id: c_uint) -> f32 { 0.0 } + /// Gets current framebuffer which is to be rendered to. + /// Could change every frame potentially. + fn hw_get_current_framebuffer(&mut self) -> Option { None } + /// Get a symbol from HW context. + fn hw_get_proc_address(&mut self, sym: &str) -> Option<*const ()> { None } } pub trait LibretroWrapperAccess { @@ -486,7 +493,7 @@ impl StaticCallbacks { .replace(hwr.context_destroy); hwr.get_current_framebuffer = Self::hw_get_current_framebuffer_fn; hwr.get_proc_address = Self::hw_get_proc_address_fn; - false // TODO: finish + handler.set_hw_render(hwr).unwrap_or(false) } EnvCmd::GetVariable => { let mut var = Self::from_void::(data)?; @@ -602,6 +609,8 @@ impl StaticCallbacks { } EnvCmd::GetUsername => Self::string_into_void(data, handler.get_username()?)?, EnvCmd::GetLanguage => Self::clone_into_void(data, &handler.get_language()?)?, + // TODO EnvCmd::GetCurrentSoftwareFramebuffer => {} + // TODO EnvCmd::GetHwRenderInterface => {} // TODO (not in libretro-sys) EnvCmd::SetSerializationQuirks => handler.set_serialization_quirks(Self::from_void(data)?), x => { if cfg!(debug) { @@ -623,13 +632,16 @@ impl StaticCallbacks { pitch: usize, ) { if let Some(cb) = unsafe { CB_SINGLETON.handler.as_mut() } { - if data.is_null() { - cb.video_refresh_dupe(width, height, pitch as c_uint); - } else if data != HW_FRAME_BUFFER_VALID { - let data = data as *const u8; - let len = pitch * (height as usize); - let slice = unsafe { from_raw_parts(data, len) }; - cb.video_refresh(slice, width, height, pitch as c_uint); + const NULL: *const c_void = std::ptr::null(); + match data { + NULL => cb.video_refresh_dupe(width, height, pitch as c_uint), + HW_FRAME_BUFFER_VALID => cb.video_refresh_hw(width, height), + data => { + let data = data as *const u8; + let len = pitch * (height as usize); + let slice = unsafe { from_raw_parts(data, len) }; + cb.video_refresh(slice, width, height, pitch as c_uint); + } } } } @@ -681,27 +693,27 @@ impl StaticCallbacks { } extern "C" fn perf_get_time_usec_cb() -> Time { unsafe { - CB_SINGLETON.handler.as_mut().map(|cb| cb.perf_get_time_usec_cb()) + CB_SINGLETON.handler.as_mut().map(|cb| cb.perf_get_time_usec()) }.unwrap_or_default() } extern "C" fn perf_get_counter_cb() -> PerfTick { unsafe { - CB_SINGLETON.handler.as_mut().map(|cb| cb.perf_get_counter_cb()) + CB_SINGLETON.handler.as_mut().map(|cb| cb.perf_get_counter()) }.unwrap_or_default() } extern "C" fn perf_get_cpu_features_cb() -> u64 { unsafe { - CB_SINGLETON.handler.as_mut().map(|cb| cb.perf_get_cpu_features_cb()) + CB_SINGLETON.handler.as_mut().map(|cb| cb.perf_get_cpu_features()) }.unwrap_or_default() } extern "C" fn perf_log_cb() { - unsafe { CB_SINGLETON.handler.as_mut().map(|cb| cb.perf_log_cb()); } + unsafe { CB_SINGLETON.handler.as_mut().map(|cb| cb.perf_log()); } } extern "C" fn perf_register_cb(counter: *mut PerfCounter) { unsafe { match (CB_SINGLETON.handler.as_mut(), counter.as_mut()) { (Some(cb), Some(counter)) => { - cb.perf_register_cb(counter); + cb.perf_register(counter); } _ => {} } @@ -711,7 +723,7 @@ impl StaticCallbacks { unsafe { match (CB_SINGLETON.handler.as_mut(), counter.as_mut()) { (Some(cb), Some(counter)) => { - cb.perf_start_cb(counter); + cb.perf_start(counter); } _ => {} } @@ -721,7 +733,7 @@ impl StaticCallbacks { unsafe { match (CB_SINGLETON.handler.as_mut(), counter.as_mut()) { (Some(cb), Some(counter)) => { - cb.perf_stop_cb(counter); + cb.perf_stop(counter); } _ => {} } @@ -739,14 +751,21 @@ impl StaticCallbacks { }.unwrap_or_default() } - // TODO: trait methods, etc. - extern "C" fn hw_dummy_fn() {} - extern "C" fn hw_get_proc_address_fn(_sym: *const c_char) -> ProcAddressFn { - Self::hw_dummy_fn // FIXME: obvious hack + extern "C" fn hw_get_proc_address_fn(sym: *const c_char) -> ProcAddressFn { + unsafe { + std::mem::transmute( + CB_SINGLETON.handler.as_mut() + .and_then(|cb| cb.hw_get_proc_address(CStr::from_ptr(sym).to_str().unwrap())) + .unwrap_or(std::ptr::null()) + ) + } } - // note: libretro.h claims this is obsolete + // note: libretro.h claims this is obsolete, but (at least) paraLLEl-n64 uses it extern "C" fn hw_get_current_framebuffer_fn() -> usize { - 0 + unsafe { + CB_SINGLETON.handler.as_mut() + .and_then(|cb| cb.hw_get_current_framebuffer()) + }.unwrap_or_default() } } diff --git a/ferretro_components/examples/multifunction_emulator.rs b/ferretro_components/examples/multifunction_emulator.rs index 4e252e3..e14375b 100644 --- a/ferretro_components/examples/multifunction_emulator.rs +++ b/ferretro_components/examples/multifunction_emulator.rs @@ -41,7 +41,8 @@ pub fn main() { let mut sdl_context = sdl2::init().unwrap(); - let sdl2_canvas = SimpleSdl2CanvasComponent::new(&mut sdl_context, emu.libretro_core()); + //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_audio = SimpleSdl2AudioComponent::new(&mut sdl_context, emu.libretro_core()); @@ -67,6 +68,7 @@ pub fn main() { emu.register_component(ffmpeg_comp); } + emu.init().unwrap(); emu.load_game(&opt.rom).unwrap(); if let Some(state) = opt.state { emu.unserialize_path(state).unwrap(); diff --git a/ferretro_components/src/base/mod.rs b/ferretro_components/src/base/mod.rs index c2357a2..0587f6f 100644 --- a/ferretro_components/src/base/mod.rs +++ b/ferretro_components/src/base/mod.rs @@ -36,10 +36,12 @@ pub type Result = std::result::Result>; #[rustfmt::skip] #[allow(unused_variables)] pub trait RetroComponent: RetroCallbacks { - fn pre_run(&mut self, retro: &mut LibretroWrapper) -> ControlFlow { ControlFlow::Continue } - fn post_run(&mut self, retro: &mut LibretroWrapper) -> ControlFlow { ControlFlow::Continue } + fn pre_init(&mut self, retro: &mut LibretroWrapper) -> Result<()> { Ok(()) } + fn post_init(&mut self, retro: &mut LibretroWrapper) -> Result<()> { Ok(()) } fn pre_load_game(&mut self, retro: &mut LibretroWrapper, rom: &Path) -> Result<()> { Ok(()) } fn post_load_game(&mut self, retro: &mut LibretroWrapper, rom: &Path) -> Result<()> { Ok(()) } + fn pre_run(&mut self, retro: &mut LibretroWrapper) -> ControlFlow { ControlFlow::Continue } + fn post_run(&mut self, retro: &mut LibretroWrapper) -> ControlFlow { ControlFlow::Continue } } impl RetroComponentBase { @@ -68,10 +70,23 @@ impl RetroComponentBase { let mut pin_emu = Box::pin(emu); ferretro_base::retro::wrapper::set_handler(pin_emu.as_mut()); - pin_emu.retro.init(); pin_emu } + pub fn init(&mut self) -> Result<()> { + for comp in &mut self.components { + comp.pre_init(&mut self.retro)?; + } + + self.retro.init(); + + for comp in &mut self.components { + comp.post_init(&mut self.retro)?; + } + + Ok(()) + } + pub fn register_component(&mut self, comp: T) -> Option<()> // TODO: Result where T: RetroComponent { @@ -434,45 +449,45 @@ impl RetroCallbacks for RetroComponentBase { .fold(false, |x, y| x || y) // not "any" because we don't short-circuit } - fn perf_get_time_usec_cb(&mut self) -> Time { + fn perf_get_time_usec(&mut self) -> Time { self.components.first_mut() - .map(|comp| comp.perf_get_time_usec_cb()) + .map(|comp| comp.perf_get_time_usec()) .unwrap_or_default() } - fn perf_get_counter_cb(&mut self) -> PerfTick { + fn perf_get_counter(&mut self) -> PerfTick { self.components.first_mut() - .map(|comp| comp.perf_get_counter_cb()) + .map(|comp| comp.perf_get_counter()) .unwrap_or_default() } - fn perf_get_cpu_features_cb(&mut self) -> u64 { + fn perf_get_cpu_features(&mut self) -> u64 { self.components.first_mut() - .map(|comp| comp.perf_get_cpu_features_cb()) + .map(|comp| comp.perf_get_cpu_features()) .unwrap_or_default() } - fn perf_log_cb(&mut self) { + fn perf_log(&mut self) { if let Some(comp) = self.components.first_mut() { - comp.perf_log_cb() + comp.perf_log() } } - fn perf_register_cb(&mut self, counter: &mut PerfCounter) { + fn perf_register(&mut self, counter: &mut PerfCounter) { if let Some(comp) = self.components.first_mut() { - comp.perf_register_cb(counter) + comp.perf_register(counter) } } - fn perf_start_cb(&mut self, counter: &mut PerfCounter) { + fn perf_start(&mut self, counter: &mut PerfCounter) { if let Some(comp) = self.components.first_mut() { - comp.perf_start_cb(counter) + comp.perf_start(counter) } } - fn perf_stop_cb(&mut self, counter: &mut PerfCounter) { + fn perf_stop(&mut self, counter: &mut PerfCounter) { if let Some(comp) = self.components.first_mut() { - comp.perf_stop_cb(counter) + comp.perf_stop(counter) } } @@ -489,6 +504,20 @@ impl RetroCallbacks for RetroComponentBase { .last() .unwrap_or_default() } + + fn hw_get_current_framebuffer(&mut self) -> Option { + self.components.iter_mut() + .map(|comp| comp.hw_get_current_framebuffer()) + .flatten() + .next() + } + + fn hw_get_proc_address(&mut self, sym: &str) -> Option<*const ()> { + self.components.iter_mut() + .map(|comp| comp.hw_get_proc_address(sym)) + .flatten() + .next() + } } impl Drop for RetroComponentBase { diff --git a/ferretro_components/src/lib.rs b/ferretro_components/src/lib.rs index 4a14ba6..29c76cd 100644 --- a/ferretro_components/src/lib.rs +++ b/ferretro_components/src/lib.rs @@ -6,5 +6,5 @@ pub mod prelude { pub use ferretro_base::retro::constants::*; pub use ferretro_base::retro::wrapped_types::*; pub use ferretro_base::retro::wrapper::{RetroCallbacks, LibretroWrapper, LibretroWrapperAccess}; - pub use ferretro_base::retro::ffi::{PixelFormat, GameGeometry, SystemAvInfo, SystemInfo}; + pub use ferretro_base::retro::ffi::{PixelFormat, GameGeometry, HwContextResetFn, HwRenderCallback, SystemAvInfo, SystemInfo}; } diff --git a/ferretro_components/src/provided/sdl2.rs b/ferretro_components/src/provided/sdl2.rs index 1fb8cf1..c5e1c04 100644 --- a/ferretro_components/src/provided/sdl2.rs +++ b/ferretro_components/src/provided/sdl2.rs @@ -1,7 +1,9 @@ mod canvas; mod audio; mod gamepad; +mod opengl; pub use canvas::SimpleSdl2CanvasComponent; +pub use opengl::SimpleSdl2OpenglComponent; pub use audio::SimpleSdl2AudioComponent; pub use gamepad::SimpleSdl2GamepadComponent; diff --git a/ferretro_components/src/provided/sdl2/opengl.rs b/ferretro_components/src/provided/sdl2/opengl.rs new file mode 100644 index 0000000..ab381dd --- /dev/null +++ b/ferretro_components/src/provided/sdl2/opengl.rs @@ -0,0 +1,106 @@ +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}; + +pub struct SimpleSdl2OpenglComponent { + window: Window, + window_fbo: c_uint, + gl_context: GLContext, + pixel_format: sdl2::pixels::PixelFormatEnum, + hw_context_reset_fn: Option, + hw_context_destroy_fn: Option, +} + +impl SimpleSdl2OpenglComponent { + pub fn new(sdl_context: &mut Sdl, retro: &LibretroWrapper) -> Result> { + let sys_info = retro.get_system_info(); + let title = format!( + "{} - ferretro", + unsafe { CStr::from_ptr(sys_info.library_name) }.to_string_lossy() + ); + + let geometry = retro.get_system_av_info().geometry; + let pixel_format = sdl2::pixels::PixelFormatEnum::ARGB1555; + + let video = sdl_context.video()?; + let window = video + .window(title.as_str(), geometry.base_width, geometry.base_height) + .opengl() + .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()?; + let mut window_fbo: c_uint = 0; + unsafe { + const GL_FRAMEBUFFER_BINDING: c_uint = 0x8CA6; + #[allow(non_snake_case)] + let glGetIntegerv: unsafe extern "C" fn(c_uint, *mut c_uint) = std::mem::transmute( + video.gl_get_proc_address("glGetIntegerv") + ); + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mut window_fbo); + } + + Ok(SimpleSdl2OpenglComponent { + window, + window_fbo, + gl_context, + pixel_format, + hw_context_reset_fn: None, + hw_context_destroy_fn: None, + }) + } +} + +impl RetroCallbacks for SimpleSdl2OpenglComponent { + 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, + }; + Some(true) + } + + fn set_hw_render(&mut self, hw_render_callback: &HwRenderCallback) -> Option { + 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_reset_fn.map(|f| unsafe { f() }); + + Some(true) + } + + /* + fn set_system_av_info(&mut self, av_info: &SystemAvInfo) -> Option { + self.set_geometry(&av_info.geometry); + Some(true) + } + + fn set_geometry(&mut self, geom: &GameGeometry) -> Option { + 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); + Some(true) + } + */ + + fn hw_get_current_framebuffer(&mut self) -> Option { + Some(self.window_fbo as usize) + } + + fn hw_get_proc_address(&mut self, sym: &str) -> Option<*const ()> { + Some(self.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 + } +} From 2b3409318b60700cbc20e986ad2c5dc1572b55ef Mon Sep 17 00:00:00 2001 From: lifning <> Date: Fri, 20 Aug 2021 21:47:26 -0700 Subject: [PATCH 2/6] trace api-logging component --- ferretro_components/src/provided/stdlib.rs | 127 +++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/ferretro_components/src/provided/stdlib.rs b/ferretro_components/src/provided/stdlib.rs index 8476d7e..6d204b4 100644 --- a/ferretro_components/src/provided/stdlib.rs +++ b/ferretro_components/src/provided/stdlib.rs @@ -3,6 +3,9 @@ use std::time::{Duration, Instant}; 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 { @@ -76,9 +79,133 @@ pub struct StderrCallTraceComponent { impl RetroComponent for StderrCallTraceComponent {} impl RetroCallbacks for StderrCallTraceComponent { + fn video_refresh(&mut self, data: &[u8], width: c_uint, height: c_uint, pitch: c_uint) { + eprintln!("{}video_refresh([u8; {}], {}, {}, {})", self.prefix, data.len(), width, height, pitch); + } + fn video_refresh_dupe(&mut self, width: c_uint, height: c_uint, pitch: c_uint) { + eprintln!("{}video_refresh_dupe({}, {}, {})", self.prefix, width, height, pitch); + } + fn video_refresh_hw(&mut self, width: c_uint, height: c_uint) { + eprintln!("{}video_refresh_hw({}, {})", self.prefix, width, height); + } fn audio_sample(&mut self, left: i16, right: i16) { eprintln!("{}audio_sample({}, {})", self.prefix, left, right); } + fn audio_sample_batch(&mut self, stereo_pcm: &[i16]) -> usize { + eprintln!("{}audio_sample_batch([i16; {}])", self.prefix, stereo_pcm.len()); + 0 + } + fn input_poll(&mut self) { + eprintln!("{}input_poll()", self.prefix); + } + fn input_state(&mut self, port: u32, device: InputDeviceId, index: InputIndex) -> i16 { + eprintln!("{}input_state({}, {:?}, {:?})", self.prefix, port, device, index); + 0 + } + fn set_rotation(&mut self, rotation: EnvRotation) -> Option { + eprintln!("{}set_rotation({:?})", self.prefix, rotation); + None + } + fn get_overscan(&mut self) -> Option { + eprintln!("{}get_overscan()", self.prefix); + None + } + fn set_message(&mut self, message: &Message) -> Option { + eprintln!("{}set_message({:?})", self.prefix, message); + None + } + fn shutdown(&mut self) -> Option { + eprintln!("{}shutdown()", self.prefix); + None + } + fn set_performance_level(&mut self, level: c_uint) -> Option { + eprintln!("{}set_performance_level({})", self.prefix, level); + None + } + fn get_system_directory(&mut self) -> Option { + eprintln!("{}get_system_directory()", self.prefix); + None + } + fn set_pixel_format(&mut self, format: PixelFormat) -> Option { + eprintln!("{}set_pixel_format({:?})", self.prefix, format); + None + } + fn set_input_descriptors(&mut self, input_descriptors: &Vec) -> Option { + eprintln!("{}set_input_descriptors(vec![InputDescriptor2; {}])", self.prefix, input_descriptors.len()); + None + } + fn set_hw_render(&mut self, hw_render_callback: &HwRenderCallback) -> Option { + eprintln!("{}set_hw_render({:?})", self.prefix, hw_render_callback); + None + } + fn get_variable(&mut self, key: &str) -> Option { + eprintln!("{}get_variable({:?})", self.prefix, key); + None + } + fn set_variables(&mut self, variables: &Vec) -> Option { + eprintln!("{}set_variables(vec![Variable2; {}])", self.prefix, variables.len()); + None + } + fn get_variable_update(&mut self) -> Option { + eprintln!("{}get_variable_update()", self.prefix); + None + } + fn set_support_no_game(&mut self, supports_no_game: bool) -> Option { + eprintln!("{}set_support_no_game({})", self.prefix, supports_no_game); + None + } + fn get_libretro_path(&mut self) -> Option { + eprintln!("{}get_libretro_path()", self.prefix); + None + } + fn get_input_device_capabilities(&mut self) -> Option { + eprintln!("{}get_input_device_capabilities()", self.prefix); + None + } + fn get_core_assets_directory(&mut self) -> Option { + eprintln!("{}get_core_assets_directory()", self.prefix); + None + } + fn get_save_directory(&mut self) -> Option { + eprintln!("{}get_save_directory()", self.prefix); + None + } + fn set_system_av_info(&mut self, system_av_info: &SystemAvInfo) -> Option { + eprintln!("{}set_system_av_info({:?})", self.prefix, system_av_info); + None + } + fn set_subsystem_info(&mut self, subsystem_info: &Vec) -> Option { + eprintln!("{}set_subsystem_info(vec![SubsystemInfo2; {}])", self.prefix, subsystem_info.len()); + None + } + fn set_controller_info(&mut self, controller_info: &Vec) -> Option { + eprintln!("{}set_controller_info(vec![ControllerDescription2; {}])", self.prefix, controller_info.len()); + None + } + fn set_memory_maps(&mut self, memory_map: &MemoryMap) -> Option { + eprintln!("{}set_memory_maps({:?})", self.prefix, memory_map); + None + } + fn set_geometry(&mut self, game_geometry: &GameGeometry) -> Option { + eprintln!("{}set_geometry({:?})", self.prefix, game_geometry); + None + } + fn get_username(&mut self) -> Option { + eprintln!("{}get_username()", self.prefix); + None + } + fn get_language(&mut self) -> Option { + eprintln!("{}get_language()", self.prefix); + None + } + fn hw_get_current_framebuffer(&mut self) -> Option { + eprintln!("{}hw_get_current_framebuffer()", self.prefix); + None + } + fn hw_get_proc_address(&mut self, sym: &str) -> Option<*const ()> { + eprintln!("{}hw_get_proc_address({:?})", self.prefix, sym); + None + } // TODO: etc... } From f954fe9ffb2237795da79eb249be5409beae30ae Mon Sep 17 00:00:00 2001 From: lifning <> Date: Fri, 20 Aug 2021 21:47:48 -0700 Subject: [PATCH 3/6] tell parallel-n64 to use glide64 when using the SDL_GL component --- .../examples/multifunction_emulator.rs | 3 +- ferretro_components/src/lib.rs | 2 +- .../src/provided/sdl2/canvas.rs | 2 +- .../src/provided/sdl2/opengl.rs | 56 +++++++++++++------ 4 files changed, 43 insertions(+), 20 deletions(-) diff --git a/ferretro_components/examples/multifunction_emulator.rs b/ferretro_components/examples/multifunction_emulator.rs index e14375b..46bdd2e 100644 --- a/ferretro_components/examples/multifunction_emulator.rs +++ b/ferretro_components/examples/multifunction_emulator.rs @@ -41,6 +41,8 @@ pub fn main() { let mut sdl_context = sdl2::init().unwrap(); + 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); @@ -53,7 +55,6 @@ pub fn main() { let sleep_fps = SleepFramerateLimitComponent::new(emu.libretro_core()); emu.register_component(sleep_fps); - emu.register_component(StderrLogComponent::default()); emu.register_component(PathBufComponent { sys_path: opt.system.clone(), libretro_path: Some(opt.core.to_path_buf()), diff --git a/ferretro_components/src/lib.rs b/ferretro_components/src/lib.rs index 29c76cd..0fc18aa 100644 --- a/ferretro_components/src/lib.rs +++ b/ferretro_components/src/lib.rs @@ -6,5 +6,5 @@ pub mod prelude { pub use ferretro_base::retro::constants::*; pub use ferretro_base::retro::wrapped_types::*; pub use ferretro_base::retro::wrapper::{RetroCallbacks, LibretroWrapper, LibretroWrapperAccess}; - pub use ferretro_base::retro::ffi::{PixelFormat, GameGeometry, HwContextResetFn, HwRenderCallback, SystemAvInfo, SystemInfo}; + pub use ferretro_base::retro::ffi::{PixelFormat, GameGeometry, HwContextResetFn, HwRenderCallback, SystemAvInfo, SystemInfo, MemoryMap}; } diff --git a/ferretro_components/src/provided/sdl2/canvas.rs b/ferretro_components/src/provided/sdl2/canvas.rs index ae12a3f..f69e9d4 100644 --- a/ferretro_components/src/provided/sdl2/canvas.rs +++ b/ferretro_components/src/provided/sdl2/canvas.rs @@ -16,7 +16,7 @@ impl SimpleSdl2CanvasComponent { pub fn new(sdl_context: &mut Sdl, retro: &LibretroWrapper) -> Self { let sys_info = retro.get_system_info(); let title = format!( - "{} - ferretro", + "{} - ferretro SDL", unsafe { CStr::from_ptr(sys_info.library_name) }.to_string_lossy() ); diff --git a/ferretro_components/src/provided/sdl2/opengl.rs b/ferretro_components/src/provided/sdl2/opengl.rs index ab381dd..75c3fbe 100644 --- a/ferretro_components/src/provided/sdl2/opengl.rs +++ b/ferretro_components/src/provided/sdl2/opengl.rs @@ -4,7 +4,7 @@ use crate::prelude::*; use std::ffi::CStr; use std::os::raw::c_uint; -use sdl2::Sdl; +use sdl2::{Sdl, VideoSubsystem}; use sdl2::video::{GLContext, Window}; pub struct SimpleSdl2OpenglComponent { @@ -20,7 +20,7 @@ impl SimpleSdl2OpenglComponent { pub fn new(sdl_context: &mut Sdl, retro: &LibretroWrapper) -> Result> { let sys_info = retro.get_system_info(); let title = format!( - "{} - ferretro", + "{} - ferretro SDL GL", unsafe { CStr::from_ptr(sys_info.library_name) }.to_string_lossy() ); @@ -36,15 +36,7 @@ impl SimpleSdl2OpenglComponent { // 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()?; - let mut window_fbo: c_uint = 0; - unsafe { - const GL_FRAMEBUFFER_BINDING: c_uint = 0x8CA6; - #[allow(non_snake_case)] - let glGetIntegerv: unsafe extern "C" fn(c_uint, *mut c_uint) = std::mem::transmute( - video.gl_get_proc_address("glGetIntegerv") - ); - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mut window_fbo); - } + let window_fbo = Self::current_framebuffer_binding(&video); Ok(SimpleSdl2OpenglComponent { window, @@ -55,6 +47,28 @@ impl SimpleSdl2OpenglComponent { hw_context_destroy_fn: None, }) } + + fn current_framebuffer_binding(video: &VideoSubsystem) -> c_uint { + let mut fbo: c_uint = 0; + unsafe { + const GL_FRAMEBUFFER_BINDING: c_uint = 0x8CA6; + #[allow(non_snake_case)] + let glGetIntegerv: unsafe extern "C" fn(c_uint, *mut c_uint) = std::mem::transmute( + video.gl_get_proc_address("glGetIntegerv") + ); + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mut fbo); + } + eprintln!("get_framebuffer_binding: {}", fbo); + fbo + } + + fn call_context_reset(&self) -> Option<()> { + self.hw_context_reset_fn.map(|f| unsafe { f(); }) + } + + fn call_context_destroy(&self) -> Option<()> { + self.hw_context_destroy_fn.map(|f| unsafe { f(); }) + } } impl RetroCallbacks for SimpleSdl2OpenglComponent { @@ -71,23 +85,31 @@ impl RetroCallbacks for SimpleSdl2OpenglComponent { 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_reset_fn.map(|f| unsafe { f() }); + self.call_context_reset(); Some(true) } - /* + fn get_variable(&mut self, key: &str) -> Option { + match key { + "parallel-n64-gfxplugin" => Some("glide64".to_string()), + _ => None, + } + } + fn set_system_av_info(&mut self, av_info: &SystemAvInfo) -> Option { - self.set_geometry(&av_info.geometry); + self.set_geometry(&av_info.geometry)?; Some(true) } fn set_geometry(&mut self, geom: &GameGeometry) -> Option { - 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.set_size(geom.base_width, geom.base_height).ok()?; + self.call_context_destroy()?; + self.gl_context = self.window.gl_create_context().ok()?; + self.window_fbo = Self::current_framebuffer_binding(self.window.subsystem()); + self.call_context_reset()?; Some(true) } - */ fn hw_get_current_framebuffer(&mut self) -> Option { Some(self.window_fbo as usize) From 0c5d0b5338146fc85a711b2a3a354a9ee9136561 Mon Sep 17 00:00:00 2001 From: lifning <> Date: Fri, 20 Aug 2021 22:17:38 -0700 Subject: [PATCH 4/6] clear color buffer on vid refresh --- ferretro_components/src/base/mod.rs | 6 ++++ .../src/provided/sdl2/opengl.rs | 36 ++++++++++++++----- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/ferretro_components/src/base/mod.rs b/ferretro_components/src/base/mod.rs index 0587f6f..9d68197 100644 --- a/ferretro_components/src/base/mod.rs +++ b/ferretro_components/src/base/mod.rs @@ -210,6 +210,12 @@ impl RetroCallbacks for RetroComponentBase { } } + fn video_refresh_hw(&mut self, width: c_uint, height: c_uint) { + for comp in &mut self.components { + comp.video_refresh_hw(width, height); + } + } + fn audio_sample(&mut self, left: i16, right: i16) { for comp in &mut self.components { comp.audio_sample(left, right); diff --git a/ferretro_components/src/provided/sdl2/opengl.rs b/ferretro_components/src/provided/sdl2/opengl.rs index 75c3fbe..e288d7c 100644 --- a/ferretro_components/src/provided/sdl2/opengl.rs +++ b/ferretro_components/src/provided/sdl2/opengl.rs @@ -4,9 +4,14 @@ use crate::prelude::*; use std::ffi::CStr; use std::os::raw::c_uint; -use sdl2::{Sdl, VideoSubsystem}; +use sdl2::Sdl; use sdl2::video::{GLContext, Window}; +// 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_COLOR_BUFFER_BIT: c_uint = 0x00004000; + +#[allow(non_snake_case)] pub struct SimpleSdl2OpenglComponent { window: Window, window_fbo: c_uint, @@ -14,6 +19,8 @@ pub struct SimpleSdl2OpenglComponent { 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), + glClear: unsafe extern "C" fn(c_uint), } impl SimpleSdl2OpenglComponent { @@ -33,10 +40,19 @@ impl SimpleSdl2OpenglComponent { .opengl() .build()?; + #[allow(non_snake_case)] + let glGetIntegerv: unsafe extern "C" fn(c_uint, *mut c_uint) = unsafe { + std::mem::transmute(video.gl_get_proc_address("glGetIntegerv")) + }; + #[allow(non_snake_case)] + let glClear: unsafe extern "C" fn(c_uint) = unsafe { + std::mem::transmute(video.gl_get_proc_address("glClear")) + }; + // 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()?; - let window_fbo = Self::current_framebuffer_binding(&video); + let window_fbo = unsafe { let mut fbo = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mut fbo); fbo }; Ok(SimpleSdl2OpenglComponent { window, @@ -45,18 +61,16 @@ impl SimpleSdl2OpenglComponent { pixel_format, hw_context_reset_fn: None, hw_context_destroy_fn: None, + glGetIntegerv, + glClear, }) } - fn current_framebuffer_binding(video: &VideoSubsystem) -> c_uint { + fn current_framebuffer_binding(&self) -> c_uint { let mut fbo: c_uint = 0; unsafe { - const GL_FRAMEBUFFER_BINDING: c_uint = 0x8CA6; #[allow(non_snake_case)] - let glGetIntegerv: unsafe extern "C" fn(c_uint, *mut c_uint) = std::mem::transmute( - video.gl_get_proc_address("glGetIntegerv") - ); - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mut fbo); + (self.glGetIntegerv)(GL_FRAMEBUFFER_BINDING, &mut fbo); } eprintln!("get_framebuffer_binding: {}", fbo); fbo @@ -72,6 +86,10 @@ impl SimpleSdl2OpenglComponent { } impl RetroCallbacks for SimpleSdl2OpenglComponent { + fn video_refresh_hw(&mut self, _width: c_uint, _height: c_uint) { + unsafe { (self.glClear)(GL_COLOR_BUFFER_BIT); } + } + fn set_pixel_format(&mut self, pix_fmt: PixelFormat) -> Option { self.pixel_format = match pix_fmt { PixelFormat::ARGB1555 => sdl2::pixels::PixelFormatEnum::RGB555, @@ -106,7 +124,7 @@ impl RetroCallbacks for SimpleSdl2OpenglComponent { self.window.set_size(geom.base_width, geom.base_height).ok()?; self.call_context_destroy()?; self.gl_context = self.window.gl_create_context().ok()?; - self.window_fbo = Self::current_framebuffer_binding(self.window.subsystem()); + self.window_fbo = self.current_framebuffer_binding(); self.call_context_reset()?; Some(true) } From 71240de38c38c9c53003a1f559b8b5703ce85f8f Mon Sep 17 00:00:00 2001 From: lifning <> Date: Sat, 21 Aug 2021 20:31:26 -0700 Subject: [PATCH 5/6] Support 2D video_refresh in the SDL+GL component ...and alter the behavior of ThreadSleep component a bit. --- .../examples/multifunction_emulator.rs | 5 +- .../src/provided/sdl2/canvas.rs | 17 ++--- .../src/provided/sdl2/opengl.rs | 72 ++++++++++++++----- ferretro_components/src/provided/stdlib.rs | 50 ++++++++----- 4 files changed, 94 insertions(+), 50 deletions(-) diff --git a/ferretro_components/examples/multifunction_emulator.rs b/ferretro_components/examples/multifunction_emulator.rs index 46bdd2e..0114afd 100644 --- a/ferretro_components/examples/multifunction_emulator.rs +++ b/ferretro_components/examples/multifunction_emulator.rs @@ -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); diff --git a/ferretro_components/src/provided/sdl2/canvas.rs b/ferretro_components/src/provided/sdl2/canvas.rs index f69e9d4..18ab497 100644 --- a/ferretro_components/src/provided/sdl2/canvas.rs +++ b/ferretro_components/src/provided/sdl2/canvas.rs @@ -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 { @@ -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 - } -} diff --git a/ferretro_components/src/provided/sdl2/opengl.rs b/ferretro_components/src/provided/sdl2/opengl.rs index e288d7c..3790344 100644 --- a/ferretro_components/src/provided/sdl2/opengl.rs +++ b/ferretro_components/src/provided/sdl2/opengl.rs @@ -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, hw_context_destroy_fn: Option, @@ -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 { 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 { - 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(); } } diff --git a/ferretro_components/src/provided/stdlib.rs b/ferretro_components/src/provided/stdlib.rs index 6d204b4..80920ac 100644 --- a/ferretro_components/src/provided/stdlib.rs +++ b/ferretro_components/src/provided/stdlib.rs @@ -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, @@ -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 { 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(); } } From 41c0864beff930bc406dc960a36541a975532f07 Mon Sep 17 00:00:00 2001 From: lifning <> Date: Sat, 21 Aug 2021 21:45:57 -0700 Subject: [PATCH 6/6] misc cleanup for SDL+GL --- ferretro_components/Cargo.toml | 3 +- .../src/provided/sdl2/canvas.rs | 15 +++---- .../src/provided/sdl2/opengl.rs | 39 +++++-------------- 3 files changed, 18 insertions(+), 39 deletions(-) diff --git a/ferretro_components/Cargo.toml b/ferretro_components/Cargo.toml index 135374b..a848122 100644 --- a/ferretro_components/Cargo.toml +++ b/ferretro_components/Cargo.toml @@ -13,6 +13,7 @@ libloading = "0.5" num_enum = "0.4" ffmpeg-next = { version = "4.3.8", optional = true } sdl2 = { version = "0.32", optional = true } +gl = { version = "0.14", optional = true } crossbeam-channel = { version = "0.4", optional = true } [dev-dependencies] @@ -21,4 +22,4 @@ structopt = "0.3" [features] ffmpeg_comp = ["ffmpeg-next"] -sdl2_comp = ["sdl2", "crossbeam-channel"] +sdl2_comp = ["sdl2", "gl", "crossbeam-channel"] diff --git a/ferretro_components/src/provided/sdl2/canvas.rs b/ferretro_components/src/provided/sdl2/canvas.rs index 18ab497..ac9e71a 100644 --- a/ferretro_components/src/provided/sdl2/canvas.rs +++ b/ferretro_components/src/provided/sdl2/canvas.rs @@ -12,7 +12,7 @@ pub struct SimpleSdl2CanvasComponent { } impl SimpleSdl2CanvasComponent { - pub fn new(sdl_context: &mut Sdl, retro: &LibretroWrapper) -> Self { + pub fn new(sdl_context: &mut Sdl, retro: &LibretroWrapper) -> Result> { let sys_info = retro.get_system_info(); let title = format!( "{} - ferretro SDL", @@ -23,19 +23,16 @@ impl SimpleSdl2CanvasComponent { let pixel_format = sdl2::pixels::PixelFormatEnum::ARGB1555; let window = sdl_context - .video() - .unwrap() + .video()? .window(title.as_str(), geometry.base_width, geometry.base_height) - .opengl() - .build() - .unwrap(); + .build()?; - let canvas = window.into_canvas().build().unwrap(); + let canvas = window.into_canvas().build()?; - SimpleSdl2CanvasComponent { + Ok(SimpleSdl2CanvasComponent { canvas, pixel_format, - } + }) } } diff --git a/ferretro_components/src/provided/sdl2/opengl.rs b/ferretro_components/src/provided/sdl2/opengl.rs index 3790344..b91bbb3 100644 --- a/ferretro_components/src/provided/sdl2/opengl.rs +++ b/ferretro_components/src/provided/sdl2/opengl.rs @@ -7,10 +7,6 @@ use sdl2::Sdl; 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; -const GL_COLOR_BUFFER_BIT: c_uint = 0x00004000; - #[allow(non_snake_case)] pub struct SimpleSdl2OpenglComponent { canvas: WindowCanvas, @@ -55,7 +51,7 @@ impl SimpleSdl2OpenglComponent { // 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. // 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 { canvas, @@ -72,7 +68,7 @@ impl SimpleSdl2OpenglComponent { let mut fbo: c_uint = 0; unsafe { #[allow(non_snake_case)] - (self.glGetIntegerv)(GL_FRAMEBUFFER_BINDING, &mut fbo); + (self.glGetIntegerv)(gl::FRAMEBUFFER_BINDING, &mut fbo); } fbo } @@ -105,7 +101,7 @@ impl RetroCallbacks for SimpleSdl2OpenglComponent { 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); } } fn set_pixel_format(&mut self, pix_fmt: PixelFormat) -> Option { @@ -119,6 +115,10 @@ impl RetroCallbacks for SimpleSdl2OpenglComponent { // TODO: depth, stencil, cache_context, bottom_left_origin? fn set_hw_render(&mut self, hw_render_callback: &HwRenderCallback) -> Option { + self.canvas.window().subsystem().gl_attr().set_context_version( + hw_render_callback.version_major as u8, + hw_render_callback.version_minor as u8, + ); 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(); @@ -138,29 +138,10 @@ impl RetroCallbacks for SimpleSdl2OpenglComponent { } fn set_geometry(&mut self, geom: &GameGeometry) -> Option { - 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); + eprintln!("set_geometry({:?})", geom); + let _ = self.canvas.window_mut().set_size(geom.base_width, geom.base_height).ok()?; + let _ = self.canvas.set_logical_size(geom.base_width, geom.base_height).ok()?; 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) }