Merge pull request 'liffy/sdl-gl' (#16) from liffy/sdl-gl into matriarch
Reviewed-on: https://git.vvn.space/cinnabon/rustro/pulls/16
This commit is contained in:
		
						commit
						409bd03621
					
				
					 11 changed files with 445 additions and 88 deletions
				
			
		| 
						 | 
					@ -6,5 +6,5 @@ pub mod prelude {
 | 
				
			||||||
    pub use crate::retro::constants::*;
 | 
					    pub use crate::retro::constants::*;
 | 
				
			||||||
    pub use crate::retro::wrapped_types::*;
 | 
					    pub use crate::retro::wrapped_types::*;
 | 
				
			||||||
    pub use crate::retro::wrapper::{RetroCallbacks, LibretroWrapper, LibretroWrapperAccess};
 | 
					    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};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -152,5 +152,7 @@ pub enum EnvCmd {
 | 
				
			||||||
    SetGeometry = ENVIRONMENT_SET_GEOMETRY,
 | 
					    SetGeometry = ENVIRONMENT_SET_GEOMETRY,
 | 
				
			||||||
    GetUsername = ENVIRONMENT_GET_USERNAME,
 | 
					    GetUsername = ENVIRONMENT_GET_USERNAME,
 | 
				
			||||||
    GetLanguage = ENVIRONMENT_GET_LANGUAGE,
 | 
					    GetLanguage = ENVIRONMENT_GET_LANGUAGE,
 | 
				
			||||||
 | 
					    GetCurrentSoftwareFramebuffer = ENVIRONMENT_GET_CURRENT_SOFTWARE_FRAMEBUFFER,
 | 
				
			||||||
 | 
					    GetHwRenderInterface = ENVIRONMENT_GET_HW_RENDER_INTERFACE,
 | 
				
			||||||
    // SetSerializationQuirks = ENVIRONMENT_SET_SERIALIZATION_QUIRKS,
 | 
					    // SetSerializationQuirks = ENVIRONMENT_SET_SERIALIZATION_QUIRKS,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,8 +45,10 @@ pub trait RetroCallbacks: Unpin + 'static {
 | 
				
			||||||
    /// Certain graphic APIs, such as OpenGL ES, do not like textures
 | 
					    /// Certain graphic APIs, such as OpenGL ES, do not like textures
 | 
				
			||||||
    /// that are not packed in memory.
 | 
					    /// that are not packed in memory.
 | 
				
			||||||
    fn video_refresh(&mut self, data: &[u8], width: c_uint, height: c_uint, pitch: c_uint) {}
 | 
					    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) {}
 | 
					    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
 | 
					    /// Renders a single audio frame. Should only be used if implementation
 | 
				
			||||||
    /// generates a single sample at a time.
 | 
					    /// generates a single sample at a time.
 | 
				
			||||||
    /// Format is signed 16-bit native endian.
 | 
					    /// 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 }
 | 
					    fn set_rumble_state(&mut self, port: c_uint, effect: RumbleEffect, strength: u16) -> bool { false }
 | 
				
			||||||
    /// Returns current time in microseconds.
 | 
					    /// Returns current time in microseconds.
 | 
				
			||||||
    /// Tries to use the most accurate timer available.
 | 
					    /// 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.
 | 
					    /// A simple counter. Usually nanoseconds, but can also be CPU cycles.
 | 
				
			||||||
    /// Can be used directly if desired (when creating a more sophisticated
 | 
					    /// Can be used directly if desired (when creating a more sophisticated
 | 
				
			||||||
    /// performance counter system).
 | 
					    /// 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_*).
 | 
					    /// 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.
 | 
					    /// Asks frontend to log and/or display the state of performance counters.
 | 
				
			||||||
    /// Performance counters can always be poked into manually as well.
 | 
					    /// 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.
 | 
					    /// Register a performance counter.
 | 
				
			||||||
    /// ident field must be set with a discrete value and other values in
 | 
					    /// ident field must be set with a discrete value and other values in
 | 
				
			||||||
    /// retro_perf_counter must be 0.
 | 
					    /// retro_perf_counter must be 0.
 | 
				
			||||||
    /// Registering can be called multiple times. To avoid calling to
 | 
					    /// Registering can be called multiple times. To avoid calling to
 | 
				
			||||||
    /// frontend redundantly, you can check registered field first.
 | 
					    /// 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.
 | 
					    /// 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.
 | 
					    /// 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 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 }
 | 
					    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<usize> { None }
 | 
				
			||||||
 | 
					    /// Get a symbol from HW context.
 | 
				
			||||||
 | 
					    fn hw_get_proc_address(&mut self, sym: &str) -> Option<*const ()> { None }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub trait LibretroWrapperAccess {
 | 
					pub trait LibretroWrapperAccess {
 | 
				
			||||||
| 
						 | 
					@ -486,7 +493,7 @@ impl StaticCallbacks {
 | 
				
			||||||
                    .replace(hwr.context_destroy);
 | 
					                    .replace(hwr.context_destroy);
 | 
				
			||||||
                hwr.get_current_framebuffer = Self::hw_get_current_framebuffer_fn;
 | 
					                hwr.get_current_framebuffer = Self::hw_get_current_framebuffer_fn;
 | 
				
			||||||
                hwr.get_proc_address = Self::hw_get_proc_address_fn;
 | 
					                hwr.get_proc_address = Self::hw_get_proc_address_fn;
 | 
				
			||||||
                false // TODO: finish
 | 
					                handler.set_hw_render(hwr).unwrap_or(false)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            EnvCmd::GetVariable => {
 | 
					            EnvCmd::GetVariable => {
 | 
				
			||||||
                let mut var = Self::from_void::<Variable>(data)?;
 | 
					                let mut var = Self::from_void::<Variable>(data)?;
 | 
				
			||||||
| 
						 | 
					@ -602,6 +609,8 @@ impl StaticCallbacks {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            EnvCmd::GetUsername => Self::string_into_void(data, handler.get_username()?)?,
 | 
					            EnvCmd::GetUsername => Self::string_into_void(data, handler.get_username()?)?,
 | 
				
			||||||
            EnvCmd::GetLanguage => Self::clone_into_void(data, &handler.get_language()?)?,
 | 
					            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)?),
 | 
					            // TODO (not in libretro-sys) EnvCmd::SetSerializationQuirks => handler.set_serialization_quirks(Self::from_void(data)?),
 | 
				
			||||||
            x => {
 | 
					            x => {
 | 
				
			||||||
                if cfg!(debug) {
 | 
					                if cfg!(debug) {
 | 
				
			||||||
| 
						 | 
					@ -623,9 +632,11 @@ impl StaticCallbacks {
 | 
				
			||||||
        pitch: usize,
 | 
					        pitch: usize,
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        if let Some(cb) = unsafe { CB_SINGLETON.handler.as_mut() } {
 | 
					        if let Some(cb) = unsafe { CB_SINGLETON.handler.as_mut() } {
 | 
				
			||||||
            if data.is_null() {
 | 
					            const NULL: *const c_void = std::ptr::null();
 | 
				
			||||||
                cb.video_refresh_dupe(width, height, pitch as c_uint);
 | 
					            match data {
 | 
				
			||||||
            } else if data != HW_FRAME_BUFFER_VALID {
 | 
					                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 data = data as *const u8;
 | 
				
			||||||
                    let len = pitch * (height as usize);
 | 
					                    let len = pitch * (height as usize);
 | 
				
			||||||
                    let slice = unsafe { from_raw_parts(data, len) };
 | 
					                    let slice = unsafe { from_raw_parts(data, len) };
 | 
				
			||||||
| 
						 | 
					@ -633,6 +644,7 @@ impl StaticCallbacks {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    extern "C" fn audio_sample_cb(left: i16, right: i16) {
 | 
					    extern "C" fn audio_sample_cb(left: i16, right: i16) {
 | 
				
			||||||
        if let Some(cb) = unsafe { CB_SINGLETON.handler.as_mut() } {
 | 
					        if let Some(cb) = unsafe { CB_SINGLETON.handler.as_mut() } {
 | 
				
			||||||
            cb.audio_sample(left, right);
 | 
					            cb.audio_sample(left, right);
 | 
				
			||||||
| 
						 | 
					@ -681,27 +693,27 @@ impl StaticCallbacks {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    extern "C" fn perf_get_time_usec_cb() -> Time {
 | 
					    extern "C" fn perf_get_time_usec_cb() -> Time {
 | 
				
			||||||
        unsafe {
 | 
					        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()
 | 
					        }.unwrap_or_default()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    extern "C" fn perf_get_counter_cb() -> PerfTick {
 | 
					    extern "C" fn perf_get_counter_cb() -> PerfTick {
 | 
				
			||||||
        unsafe {
 | 
					        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()
 | 
					        }.unwrap_or_default()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    extern "C" fn perf_get_cpu_features_cb() -> u64 {
 | 
					    extern "C" fn perf_get_cpu_features_cb() -> u64 {
 | 
				
			||||||
        unsafe {
 | 
					        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()
 | 
					        }.unwrap_or_default()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    extern "C" fn perf_log_cb() {
 | 
					    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) {
 | 
					    extern "C" fn perf_register_cb(counter: *mut PerfCounter) {
 | 
				
			||||||
        unsafe {
 | 
					        unsafe {
 | 
				
			||||||
            match (CB_SINGLETON.handler.as_mut(), counter.as_mut()) {
 | 
					            match (CB_SINGLETON.handler.as_mut(), counter.as_mut()) {
 | 
				
			||||||
                (Some(cb), Some(counter)) => {
 | 
					                (Some(cb), Some(counter)) => {
 | 
				
			||||||
                    cb.perf_register_cb(counter);
 | 
					                    cb.perf_register(counter);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                _ => {}
 | 
					                _ => {}
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -711,7 +723,7 @@ impl StaticCallbacks {
 | 
				
			||||||
        unsafe {
 | 
					        unsafe {
 | 
				
			||||||
            match (CB_SINGLETON.handler.as_mut(), counter.as_mut()) {
 | 
					            match (CB_SINGLETON.handler.as_mut(), counter.as_mut()) {
 | 
				
			||||||
                (Some(cb), Some(counter)) => {
 | 
					                (Some(cb), Some(counter)) => {
 | 
				
			||||||
                    cb.perf_start_cb(counter);
 | 
					                    cb.perf_start(counter);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                _ => {}
 | 
					                _ => {}
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -721,7 +733,7 @@ impl StaticCallbacks {
 | 
				
			||||||
        unsafe {
 | 
					        unsafe {
 | 
				
			||||||
            match (CB_SINGLETON.handler.as_mut(), counter.as_mut()) {
 | 
					            match (CB_SINGLETON.handler.as_mut(), counter.as_mut()) {
 | 
				
			||||||
                (Some(cb), Some(counter)) => {
 | 
					                (Some(cb), Some(counter)) => {
 | 
				
			||||||
                    cb.perf_stop_cb(counter);
 | 
					                    cb.perf_stop(counter);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                _ => {}
 | 
					                _ => {}
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -739,14 +751,21 @@ impl StaticCallbacks {
 | 
				
			||||||
        }.unwrap_or_default()
 | 
					        }.unwrap_or_default()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO: trait methods, etc.
 | 
					    extern "C" fn hw_get_proc_address_fn(sym: *const c_char) -> ProcAddressFn {
 | 
				
			||||||
    extern "C" fn hw_dummy_fn() {}
 | 
					        unsafe {
 | 
				
			||||||
    extern "C" fn hw_get_proc_address_fn(_sym: *const c_char) -> ProcAddressFn {
 | 
					            std::mem::transmute(
 | 
				
			||||||
        Self::hw_dummy_fn // FIXME: obvious hack
 | 
					                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 {
 | 
					    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()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,7 @@ libloading = "0.5"
 | 
				
			||||||
num_enum = "0.4"
 | 
					num_enum = "0.4"
 | 
				
			||||||
ffmpeg-next = { version = "4.3.8", optional = true }
 | 
					ffmpeg-next = { version = "4.3.8", optional = true }
 | 
				
			||||||
sdl2 = { version = "0.32", optional = true }
 | 
					sdl2 = { version = "0.32", optional = true }
 | 
				
			||||||
 | 
					gl = { version = "0.14", optional = true }
 | 
				
			||||||
crossbeam-channel = { version = "0.4", optional = true }
 | 
					crossbeam-channel = { version = "0.4", optional = true }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dev-dependencies]
 | 
					[dev-dependencies]
 | 
				
			||||||
| 
						 | 
					@ -21,4 +22,4 @@ structopt = "0.3"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[features]
 | 
					[features]
 | 
				
			||||||
ffmpeg_comp = ["ffmpeg-next"]
 | 
					ffmpeg_comp = ["ffmpeg-next"]
 | 
				
			||||||
sdl2_comp = ["sdl2", "crossbeam-channel"]
 | 
					sdl2_comp = ["sdl2", "gl", "crossbeam-channel"]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,8 +41,10 @@ pub fn main() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut sdl_context = sdl2::init().unwrap();
 | 
					    let mut sdl_context = sdl2::init().unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let sdl2_canvas = SimpleSdl2CanvasComponent::new(&mut sdl_context, emu.libretro_core());
 | 
					    emu.register_component(StderrLogComponent { prefix: "{log} ".to_string() });
 | 
				
			||||||
    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());
 | 
					    let sdl2_audio = SimpleSdl2AudioComponent::new(&mut sdl_context, emu.libretro_core());
 | 
				
			||||||
    emu.register_component(sdl2_audio);
 | 
					    emu.register_component(sdl2_audio);
 | 
				
			||||||
| 
						 | 
					@ -52,7 +54,6 @@ pub fn main() {
 | 
				
			||||||
    let sleep_fps = SleepFramerateLimitComponent::new(emu.libretro_core());
 | 
					    let sleep_fps = SleepFramerateLimitComponent::new(emu.libretro_core());
 | 
				
			||||||
    emu.register_component(sleep_fps);
 | 
					    emu.register_component(sleep_fps);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    emu.register_component(StderrLogComponent::default());
 | 
					 | 
				
			||||||
    emu.register_component(PathBufComponent {
 | 
					    emu.register_component(PathBufComponent {
 | 
				
			||||||
        sys_path: opt.system.clone(),
 | 
					        sys_path: opt.system.clone(),
 | 
				
			||||||
        libretro_path: Some(opt.core.to_path_buf()),
 | 
					        libretro_path: Some(opt.core.to_path_buf()),
 | 
				
			||||||
| 
						 | 
					@ -67,6 +68,7 @@ pub fn main() {
 | 
				
			||||||
        emu.register_component(ffmpeg_comp);
 | 
					        emu.register_component(ffmpeg_comp);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    emu.init().unwrap();
 | 
				
			||||||
    emu.load_game(&opt.rom).unwrap();
 | 
					    emu.load_game(&opt.rom).unwrap();
 | 
				
			||||||
    if let Some(state) = opt.state {
 | 
					    if let Some(state) = opt.state {
 | 
				
			||||||
        emu.unserialize_path(state).unwrap();
 | 
					        emu.unserialize_path(state).unwrap();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,10 +36,12 @@ pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
 | 
				
			||||||
#[rustfmt::skip]
 | 
					#[rustfmt::skip]
 | 
				
			||||||
#[allow(unused_variables)]
 | 
					#[allow(unused_variables)]
 | 
				
			||||||
pub trait RetroComponent: RetroCallbacks {
 | 
					pub trait RetroComponent: RetroCallbacks {
 | 
				
			||||||
    fn pre_run(&mut self, retro: &mut LibretroWrapper) -> ControlFlow { ControlFlow::Continue }
 | 
					    fn pre_init(&mut self, retro: &mut LibretroWrapper) -> Result<()> { Ok(()) }
 | 
				
			||||||
    fn post_run(&mut self, retro: &mut LibretroWrapper) -> ControlFlow { ControlFlow::Continue }
 | 
					    fn post_init(&mut self, retro: &mut LibretroWrapper) -> Result<()> { Ok(()) }
 | 
				
			||||||
    fn pre_load_game(&mut self, retro: &mut LibretroWrapper, rom: &Path) -> 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 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 {
 | 
					impl RetroComponentBase {
 | 
				
			||||||
| 
						 | 
					@ -68,10 +70,23 @@ impl RetroComponentBase {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut pin_emu = Box::pin(emu);
 | 
					        let mut pin_emu = Box::pin(emu);
 | 
				
			||||||
        ferretro_base::retro::wrapper::set_handler(pin_emu.as_mut());
 | 
					        ferretro_base::retro::wrapper::set_handler(pin_emu.as_mut());
 | 
				
			||||||
        pin_emu.retro.init();
 | 
					 | 
				
			||||||
        pin_emu
 | 
					        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<T>(&mut self, comp: T) -> Option<()> // TODO: Result
 | 
					    pub fn register_component<T>(&mut self, comp: T) -> Option<()> // TODO: Result
 | 
				
			||||||
        where T: RetroComponent
 | 
					        where T: RetroComponent
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -195,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) {
 | 
					    fn audio_sample(&mut self, left: i16, right: i16) {
 | 
				
			||||||
        for comp in &mut self.components {
 | 
					        for comp in &mut self.components {
 | 
				
			||||||
            comp.audio_sample(left, right);
 | 
					            comp.audio_sample(left, right);
 | 
				
			||||||
| 
						 | 
					@ -434,45 +455,45 @@ impl RetroCallbacks for RetroComponentBase {
 | 
				
			||||||
            .fold(false, |x, y| x || y) // not "any" because we don't short-circuit
 | 
					            .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()
 | 
					        self.components.first_mut()
 | 
				
			||||||
            .map(|comp| comp.perf_get_time_usec_cb())
 | 
					            .map(|comp| comp.perf_get_time_usec())
 | 
				
			||||||
            .unwrap_or_default()
 | 
					            .unwrap_or_default()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn perf_get_counter_cb(&mut self) -> PerfTick {
 | 
					    fn perf_get_counter(&mut self) -> PerfTick {
 | 
				
			||||||
        self.components.first_mut()
 | 
					        self.components.first_mut()
 | 
				
			||||||
            .map(|comp| comp.perf_get_counter_cb())
 | 
					            .map(|comp| comp.perf_get_counter())
 | 
				
			||||||
            .unwrap_or_default()
 | 
					            .unwrap_or_default()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn perf_get_cpu_features_cb(&mut self) -> u64 {
 | 
					    fn perf_get_cpu_features(&mut self) -> u64 {
 | 
				
			||||||
        self.components.first_mut()
 | 
					        self.components.first_mut()
 | 
				
			||||||
            .map(|comp| comp.perf_get_cpu_features_cb())
 | 
					            .map(|comp| comp.perf_get_cpu_features())
 | 
				
			||||||
            .unwrap_or_default()
 | 
					            .unwrap_or_default()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn perf_log_cb(&mut self) {
 | 
					    fn perf_log(&mut self) {
 | 
				
			||||||
         if let Some(comp) = self.components.first_mut() {
 | 
					         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() {
 | 
					         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() {
 | 
					         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() {
 | 
					         if let Some(comp) = self.components.first_mut() {
 | 
				
			||||||
             comp.perf_stop_cb(counter)
 | 
					             comp.perf_stop(counter)
 | 
				
			||||||
         }
 | 
					         }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -489,6 +510,20 @@ impl RetroCallbacks for RetroComponentBase {
 | 
				
			||||||
            .last()
 | 
					            .last()
 | 
				
			||||||
            .unwrap_or_default()
 | 
					            .unwrap_or_default()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn hw_get_current_framebuffer(&mut self) -> Option<usize> {
 | 
				
			||||||
 | 
					        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 {
 | 
					impl Drop for RetroComponentBase {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,5 +6,5 @@ pub mod prelude {
 | 
				
			||||||
    pub use ferretro_base::retro::constants::*;
 | 
					    pub use ferretro_base::retro::constants::*;
 | 
				
			||||||
    pub use ferretro_base::retro::wrapped_types::*;
 | 
					    pub use ferretro_base::retro::wrapped_types::*;
 | 
				
			||||||
    pub use ferretro_base::retro::wrapper::{RetroCallbacks, LibretroWrapper, LibretroWrapperAccess};
 | 
					    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, MemoryMap};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,9 @@
 | 
				
			||||||
mod canvas;
 | 
					mod canvas;
 | 
				
			||||||
mod audio;
 | 
					mod audio;
 | 
				
			||||||
mod gamepad;
 | 
					mod gamepad;
 | 
				
			||||||
 | 
					mod opengl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use canvas::SimpleSdl2CanvasComponent;
 | 
					pub use canvas::SimpleSdl2CanvasComponent;
 | 
				
			||||||
 | 
					pub use opengl::SimpleSdl2OpenglComponent;
 | 
				
			||||||
pub use audio::SimpleSdl2AudioComponent;
 | 
					pub use audio::SimpleSdl2AudioComponent;
 | 
				
			||||||
pub use gamepad::SimpleSdl2GamepadComponent;
 | 
					pub use gamepad::SimpleSdl2GamepadComponent;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,3 @@
 | 
				
			||||||
use crate::base::ControlFlow;
 | 
					 | 
				
			||||||
use crate::prelude::*;
 | 
					use crate::prelude::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::ffi::CStr;
 | 
					use std::ffi::CStr;
 | 
				
			||||||
| 
						 | 
					@ -13,10 +12,10 @@ pub struct SimpleSdl2CanvasComponent {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl SimpleSdl2CanvasComponent {
 | 
					impl SimpleSdl2CanvasComponent {
 | 
				
			||||||
    pub fn new(sdl_context: &mut Sdl, retro: &LibretroWrapper) -> Self {
 | 
					    pub fn new(sdl_context: &mut Sdl, retro: &LibretroWrapper) -> Result<Self, Box<dyn std::error::Error>> {
 | 
				
			||||||
        let sys_info = retro.get_system_info();
 | 
					        let sys_info = retro.get_system_info();
 | 
				
			||||||
        let title = format!(
 | 
					        let title = format!(
 | 
				
			||||||
            "{} - ferretro",
 | 
					            "{} - ferretro SDL",
 | 
				
			||||||
            unsafe { CStr::from_ptr(sys_info.library_name) }.to_string_lossy()
 | 
					            unsafe { CStr::from_ptr(sys_info.library_name) }.to_string_lossy()
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,28 +23,25 @@ impl SimpleSdl2CanvasComponent {
 | 
				
			||||||
        let pixel_format = sdl2::pixels::PixelFormatEnum::ARGB1555;
 | 
					        let pixel_format = sdl2::pixels::PixelFormatEnum::ARGB1555;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let window = sdl_context
 | 
					        let window = sdl_context
 | 
				
			||||||
            .video()
 | 
					            .video()?
 | 
				
			||||||
            .unwrap()
 | 
					 | 
				
			||||||
            .window(title.as_str(), geometry.base_width, geometry.base_height)
 | 
					            .window(title.as_str(), geometry.base_width, geometry.base_height)
 | 
				
			||||||
            .opengl()
 | 
					            .build()?;
 | 
				
			||||||
            .build()
 | 
					 | 
				
			||||||
            .unwrap();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let canvas = window.into_canvas().build().unwrap();
 | 
					        let canvas = window.into_canvas().build()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        SimpleSdl2CanvasComponent {
 | 
					        Ok(SimpleSdl2CanvasComponent {
 | 
				
			||||||
            canvas,
 | 
					            canvas,
 | 
				
			||||||
            pixel_format,
 | 
					            pixel_format,
 | 
				
			||||||
        }
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
| 
						 | 
					@ -54,6 +50,7 @@ impl RetroCallbacks for SimpleSdl2CanvasComponent {
 | 
				
			||||||
                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 +80,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
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										161
									
								
								ferretro_components/src/provided/sdl2/opengl.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								ferretro_components/src/provided/sdl2/opengl.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,161 @@
 | 
				
			||||||
 | 
					use crate::prelude::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::ffi::CStr;
 | 
				
			||||||
 | 
					use std::os::raw::c_uint;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use sdl2::Sdl;
 | 
				
			||||||
 | 
					use sdl2::rect::Rect;
 | 
				
			||||||
 | 
					use sdl2::render::WindowCanvas;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[allow(non_snake_case)]
 | 
				
			||||||
 | 
					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),
 | 
				
			||||||
 | 
					    glClear: unsafe extern "C" fn(c_uint),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SimpleSdl2OpenglComponent {
 | 
				
			||||||
 | 
					    pub fn new(sdl_context: &mut Sdl, retro: &LibretroWrapper) -> Result<Self, Box<dyn std::error::Error>> {
 | 
				
			||||||
 | 
					        let sys_info = retro.get_system_info();
 | 
				
			||||||
 | 
					        let title = format!(
 | 
				
			||||||
 | 
					            "{} - ferretro SDL GL",
 | 
				
			||||||
 | 
					            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()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[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"))
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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.
 | 
				
			||||||
 | 
					        // 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 {
 | 
				
			||||||
 | 
					            canvas,
 | 
				
			||||||
 | 
					            window_fbo,
 | 
				
			||||||
 | 
					            pixel_format,
 | 
				
			||||||
 | 
					            hw_context_reset_fn: None,
 | 
				
			||||||
 | 
					            hw_context_destroy_fn: None,
 | 
				
			||||||
 | 
					            glGetIntegerv,
 | 
				
			||||||
 | 
					            glClear,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn current_framebuffer_binding(&self) -> c_uint {
 | 
				
			||||||
 | 
					        let mut fbo: c_uint = 0;
 | 
				
			||||||
 | 
					        unsafe {
 | 
				
			||||||
 | 
					            #[allow(non_snake_case)]
 | 
				
			||||||
 | 
					            (self.glGetIntegerv)(gl::FRAMEBUFFER_BINDING, &mut 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 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); }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        Some(true)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO: depth, stencil, cache_context, bottom_left_origin?
 | 
				
			||||||
 | 
					    fn set_hw_render(&mut self, hw_render_callback: &HwRenderCallback) -> Option<bool> {
 | 
				
			||||||
 | 
					        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();
 | 
				
			||||||
 | 
					        Some(true)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn get_variable(&mut self, key: &str) -> Option<String> {
 | 
				
			||||||
 | 
					        match key {
 | 
				
			||||||
 | 
					            "parallel-n64-gfxplugin" => Some("glide64".to_string()),
 | 
				
			||||||
 | 
					            _ => None,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn set_system_av_info(&mut self, av_info: &SystemAvInfo) -> Option<bool> {
 | 
				
			||||||
 | 
					        self.set_geometry(&av_info.geometry)?;
 | 
				
			||||||
 | 
					        Some(true)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn set_geometry(&mut self, geom: &GameGeometry) -> Option<bool> {
 | 
				
			||||||
 | 
					        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();
 | 
				
			||||||
 | 
					        Some(true)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn hw_get_current_framebuffer(&mut self) -> Option<usize> {
 | 
				
			||||||
 | 
					        Some(self.window_fbo as usize)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn hw_get_proc_address(&mut self, sym: &str) -> Option<*const ()> {
 | 
				
			||||||
 | 
					        Some(self.canvas.window().subsystem().gl_get_proc_address(sym))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Drop for SimpleSdl2OpenglComponent {
 | 
				
			||||||
 | 
					    fn drop(&mut self) {
 | 
				
			||||||
 | 
					        self.call_context_destroy();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,10 @@
 | 
				
			||||||
 | 
					use std::os::raw::c_uint;
 | 
				
			||||||
use std::path::PathBuf;
 | 
					use std::path::PathBuf;
 | 
				
			||||||
use std::time::{Duration, Instant};
 | 
					use std::time::{Duration, Instant};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::base::ControlFlow;
 | 
					use crate::base::ControlFlow;
 | 
				
			||||||
use crate::prelude::*;
 | 
					use crate::prelude::*;
 | 
				
			||||||
 | 
					use ferretro_base::retro::ffi::{Message, Language};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Default)]
 | 
					#[derive(Default)]
 | 
				
			||||||
pub struct PathBufComponent {
 | 
					pub struct PathBufComponent {
 | 
				
			||||||
| 
						 | 
					@ -76,30 +78,180 @@ pub struct StderrCallTraceComponent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl RetroComponent for StderrCallTraceComponent {}
 | 
					impl RetroComponent for StderrCallTraceComponent {}
 | 
				
			||||||
impl RetroCallbacks 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) {
 | 
					    fn audio_sample(&mut self, left: i16, right: i16) {
 | 
				
			||||||
        eprintln!("{}audio_sample({}, {})", self.prefix, left, right);
 | 
					        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<bool> {
 | 
				
			||||||
 | 
					        eprintln!("{}set_rotation({:?})", self.prefix, rotation);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn get_overscan(&mut self) -> Option<bool> {
 | 
				
			||||||
 | 
					        eprintln!("{}get_overscan()", self.prefix);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn set_message(&mut self, message: &Message) -> Option<bool> {
 | 
				
			||||||
 | 
					        eprintln!("{}set_message({:?})", self.prefix, message);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn shutdown(&mut self) -> Option<bool> {
 | 
				
			||||||
 | 
					        eprintln!("{}shutdown()", self.prefix);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn set_performance_level(&mut self, level: c_uint) -> Option<bool> {
 | 
				
			||||||
 | 
					        eprintln!("{}set_performance_level({})", self.prefix, level);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn get_system_directory(&mut self) -> Option<PathBuf> {
 | 
				
			||||||
 | 
					        eprintln!("{}get_system_directory()", self.prefix);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn set_pixel_format(&mut self, format: PixelFormat) -> Option<bool> {
 | 
				
			||||||
 | 
					        eprintln!("{}set_pixel_format({:?})", self.prefix, format);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn set_input_descriptors(&mut self, input_descriptors: &Vec<InputDescriptor2>) -> Option<bool> {
 | 
				
			||||||
 | 
					        eprintln!("{}set_input_descriptors(vec![InputDescriptor2; {}])", self.prefix, input_descriptors.len());
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn set_hw_render(&mut self, hw_render_callback: &HwRenderCallback) -> Option<bool> {
 | 
				
			||||||
 | 
					        eprintln!("{}set_hw_render({:?})", self.prefix, hw_render_callback);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn get_variable(&mut self, key: &str) -> Option<String> {
 | 
				
			||||||
 | 
					        eprintln!("{}get_variable({:?})", self.prefix, key);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn set_variables(&mut self, variables: &Vec<Variable2>) -> Option<bool> {
 | 
				
			||||||
 | 
					        eprintln!("{}set_variables(vec![Variable2; {}])", self.prefix, variables.len());
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn get_variable_update(&mut self) -> Option<bool> {
 | 
				
			||||||
 | 
					        eprintln!("{}get_variable_update()", self.prefix);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn set_support_no_game(&mut self, supports_no_game: bool) -> Option<bool> {
 | 
				
			||||||
 | 
					        eprintln!("{}set_support_no_game({})", self.prefix, supports_no_game);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn get_libretro_path(&mut self) -> Option<PathBuf> {
 | 
				
			||||||
 | 
					        eprintln!("{}get_libretro_path()", self.prefix);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn get_input_device_capabilities(&mut self) -> Option<u64> {
 | 
				
			||||||
 | 
					        eprintln!("{}get_input_device_capabilities()", self.prefix);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn get_core_assets_directory(&mut self) -> Option<PathBuf> {
 | 
				
			||||||
 | 
					        eprintln!("{}get_core_assets_directory()", self.prefix);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn get_save_directory(&mut self) -> Option<PathBuf> {
 | 
				
			||||||
 | 
					        eprintln!("{}get_save_directory()", self.prefix);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn set_system_av_info(&mut self, system_av_info: &SystemAvInfo) -> Option<bool> {
 | 
				
			||||||
 | 
					        eprintln!("{}set_system_av_info({:?})", self.prefix, system_av_info);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn set_subsystem_info(&mut self, subsystem_info: &Vec<SubsystemInfo2>) -> Option<bool> {
 | 
				
			||||||
 | 
					        eprintln!("{}set_subsystem_info(vec![SubsystemInfo2; {}])", self.prefix, subsystem_info.len());
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn set_controller_info(&mut self, controller_info: &Vec<ControllerDescription2>) -> Option<bool> {
 | 
				
			||||||
 | 
					        eprintln!("{}set_controller_info(vec![ControllerDescription2; {}])", self.prefix, controller_info.len());
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn set_memory_maps(&mut self, memory_map: &MemoryMap) -> Option<bool> {
 | 
				
			||||||
 | 
					        eprintln!("{}set_memory_maps({:?})", self.prefix, memory_map);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn set_geometry(&mut self, game_geometry: &GameGeometry) -> Option<bool> {
 | 
				
			||||||
 | 
					        eprintln!("{}set_geometry({:?})", self.prefix, game_geometry);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn get_username(&mut self) -> Option<String> {
 | 
				
			||||||
 | 
					        eprintln!("{}get_username()", self.prefix);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn get_language(&mut self) -> Option<Language> {
 | 
				
			||||||
 | 
					        eprintln!("{}get_language()", self.prefix);
 | 
				
			||||||
 | 
					        None
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    fn hw_get_current_framebuffer(&mut self) -> Option<usize> {
 | 
				
			||||||
 | 
					        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...
 | 
					    // TODO: etc...
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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() {
 | 
				
			||||||
| 
						 | 
					@ -108,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…
	
	Add table
		
		Reference in a new issue