2021-08-19 11:42:21 +02:00
|
|
|
use crate::prelude::*;
|
|
|
|
|
|
|
|
use std::ffi::CStr;
|
|
|
|
use std::os::raw::c_uint;
|
|
|
|
|
2021-08-21 07:17:38 +02:00
|
|
|
use sdl2::Sdl;
|
2021-08-22 05:31:26 +02:00
|
|
|
use sdl2::rect::Rect;
|
|
|
|
use sdl2::render::WindowCanvas;
|
2021-08-19 11:42:21 +02:00
|
|
|
|
2021-08-21 07:17:38 +02:00
|
|
|
// 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)]
|
2021-08-19 11:42:21 +02:00
|
|
|
pub struct SimpleSdl2OpenglComponent {
|
2021-08-22 05:31:26 +02:00
|
|
|
canvas: WindowCanvas,
|
2021-08-19 11:42:21 +02:00
|
|
|
window_fbo: c_uint,
|
|
|
|
pixel_format: sdl2::pixels::PixelFormatEnum,
|
|
|
|
hw_context_reset_fn: Option<HwContextResetFn>,
|
|
|
|
hw_context_destroy_fn: Option<HwContextResetFn>,
|
2021-08-21 07:17:38 +02:00
|
|
|
glGetIntegerv: unsafe extern "C" fn(c_uint, *mut c_uint),
|
|
|
|
glClear: unsafe extern "C" fn(c_uint),
|
2021-08-19 11:42:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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!(
|
2021-08-21 06:47:48 +02:00
|
|
|
"{} - ferretro SDL GL",
|
2021-08-19 11:42:21 +02:00
|
|
|
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()?;
|
|
|
|
|
2021-08-21 07:17:38 +02:00
|
|
|
#[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"))
|
|
|
|
};
|
|
|
|
|
2021-08-22 05:31:26 +02:00
|
|
|
let canvas = window.into_canvas()
|
|
|
|
.accelerated()
|
|
|
|
.target_texture()
|
|
|
|
.build()?;
|
2021-08-19 11:42:21 +02:00
|
|
|
// http://forums.libsdl.org/viewtopic.php?p=43353
|
2021-08-22 05:31:26 +02:00
|
|
|
// 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.
|
2021-08-21 07:17:38 +02:00
|
|
|
let window_fbo = unsafe { let mut fbo = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mut fbo); fbo };
|
2021-08-19 11:42:21 +02:00
|
|
|
|
|
|
|
Ok(SimpleSdl2OpenglComponent {
|
2021-08-22 05:31:26 +02:00
|
|
|
canvas,
|
2021-08-19 11:42:21 +02:00
|
|
|
window_fbo,
|
|
|
|
pixel_format,
|
|
|
|
hw_context_reset_fn: None,
|
|
|
|
hw_context_destroy_fn: None,
|
2021-08-21 07:17:38 +02:00
|
|
|
glGetIntegerv,
|
|
|
|
glClear,
|
2021-08-19 11:42:21 +02:00
|
|
|
})
|
|
|
|
}
|
2021-08-21 06:47:48 +02:00
|
|
|
|
2021-08-21 07:17:38 +02:00
|
|
|
fn current_framebuffer_binding(&self) -> c_uint {
|
2021-08-21 06:47:48 +02:00
|
|
|
let mut fbo: c_uint = 0;
|
|
|
|
unsafe {
|
|
|
|
#[allow(non_snake_case)]
|
2021-08-21 07:17:38 +02:00
|
|
|
(self.glGetIntegerv)(GL_FRAMEBUFFER_BINDING, &mut fbo);
|
2021-08-21 06:47:48 +02:00
|
|
|
}
|
|
|
|
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(); })
|
|
|
|
}
|
2021-08-19 11:42:21 +02:00
|
|
|
}
|
|
|
|
|
2021-08-22 05:31:26 +02:00
|
|
|
impl RetroComponent for SimpleSdl2OpenglComponent {}
|
2021-08-19 11:42:21 +02:00
|
|
|
impl RetroCallbacks for SimpleSdl2OpenglComponent {
|
2021-08-22 05:31:26 +02:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2021-08-21 07:17:38 +02:00
|
|
|
fn video_refresh_hw(&mut self, _width: c_uint, _height: c_uint) {
|
2021-08-22 05:31:26 +02:00
|
|
|
self.canvas.present();
|
2021-08-21 07:17:38 +02:00
|
|
|
unsafe { (self.glClear)(GL_COLOR_BUFFER_BIT); }
|
|
|
|
}
|
|
|
|
|
2021-08-19 11:42:21 +02:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2021-08-22 05:31:26 +02:00
|
|
|
// TODO: depth, stencil, cache_context, bottom_left_origin?
|
2021-08-19 11:42:21 +02:00
|
|
|
fn set_hw_render(&mut self, hw_render_callback: &HwRenderCallback) -> Option<bool> {
|
|
|
|
self.hw_context_reset_fn.replace(hw_render_callback.context_reset);
|
|
|
|
self.hw_context_destroy_fn.replace(hw_render_callback.context_destroy);
|
2021-08-21 06:47:48 +02:00
|
|
|
self.call_context_reset();
|
2021-08-19 11:42:21 +02:00
|
|
|
Some(true)
|
|
|
|
}
|
|
|
|
|
2021-08-21 06:47:48 +02:00
|
|
|
fn get_variable(&mut self, key: &str) -> Option<String> {
|
|
|
|
match key {
|
|
|
|
"parallel-n64-gfxplugin" => Some("glide64".to_string()),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-19 11:42:21 +02:00
|
|
|
fn set_system_av_info(&mut self, av_info: &SystemAvInfo) -> Option<bool> {
|
2021-08-21 06:47:48 +02:00
|
|
|
self.set_geometry(&av_info.geometry)?;
|
2021-08-19 11:42:21 +02:00
|
|
|
Some(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_geometry(&mut self, geom: &GameGeometry) -> Option<bool> {
|
2021-08-22 05:31:26 +02:00
|
|
|
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();
|
2021-08-21 07:17:38 +02:00
|
|
|
self.window_fbo = self.current_framebuffer_binding();
|
2021-08-22 05:31:26 +02:00
|
|
|
|
|
|
|
std::mem::swap(&mut self.canvas, &mut new_canvas);
|
|
|
|
std::mem::forget(new_canvas);
|
|
|
|
|
2021-08-21 06:47:48 +02:00
|
|
|
self.call_context_reset()?;
|
2021-08-22 05:31:26 +02:00
|
|
|
*/
|
2021-08-19 11:42:21 +02:00
|
|
|
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 ()> {
|
2021-08-22 05:31:26 +02:00
|
|
|
Some(self.canvas.window().subsystem().gl_get_proc_address(sym))
|
2021-08-19 11:42:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-22 05:31:26 +02:00
|
|
|
impl Drop for SimpleSdl2OpenglComponent {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
self.call_context_destroy();
|
2021-08-19 11:42:21 +02:00
|
|
|
}
|
|
|
|
}
|