ferretro/ferretro_components/src/provided/sdl2/opengl.rs

181 lines
6.1 KiB
Rust
Raw Normal View History

use crate::prelude::*;
use std::ffi::CStr;
use std::os::raw::c_uint;
2021-08-21 07:17:38 +02:00
use sdl2::Sdl;
use sdl2::rect::Rect;
use sdl2::render::WindowCanvas;
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)]
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>,
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),
}
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()?;
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"))
};
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.
2021-08-21 07:17:38 +02:00
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,
2021-08-21 07:17:38 +02:00
glGetIntegerv,
glClear,
})
}
2021-08-21 07:17:38 +02:00
fn current_framebuffer_binding(&self) -> c_uint {
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);
}
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();
}
2021-08-21 07:17:38 +02:00
fn video_refresh_hw(&mut self, _width: c_uint, _height: c_uint) {
self.canvas.present();
2021-08-21 07:17:38 +02:00
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.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> {
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();
std::mem::swap(&mut self.canvas, &mut new_canvas);
std::mem::forget(new_canvas);
self.call_context_reset()?;
*/
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();
}
}