mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2024-11-21 21:22:44 +00:00
Refactor renderer initialization
This commit is contained in:
parent
0db8ab0308
commit
fb87b3d5ab
|
@ -36,48 +36,65 @@ pub trait Backend {
|
|||
pub trait BackendEventLoop {
|
||||
fn run(&mut self, game: &mut Game, ctx: &mut Context);
|
||||
|
||||
fn new_renderer(&self, ctx: *mut Context) -> GameResult<Box<dyn BackendRenderer>>;
|
||||
fn new_renderer(&self) -> GameResult<Box<dyn BackendRenderer>>;
|
||||
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
}
|
||||
|
||||
pub trait BackendRenderer {
|
||||
/// Human-readable name for the renderer. May return different values based on current platform or settings.
|
||||
fn renderer_name(&self) -> String;
|
||||
|
||||
/// Clear the current render target with the specified color.
|
||||
fn clear(&mut self, color: Color);
|
||||
|
||||
/// Present the current frame to the screen.
|
||||
fn present(&mut self) -> GameResult;
|
||||
|
||||
/// Sets the preferred frame swap mode.
|
||||
fn set_swap_mode(&mut self, _mode: SwapMode) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Prepare the renderer for drawing.
|
||||
fn prepare_draw(&mut self, _width: f32, _height: f32) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a new mutable texture with the specified dimensions.
|
||||
fn create_texture_mutable(&mut self, width: u16, height: u16) -> GameResult<Box<dyn BackendTexture>>;
|
||||
|
||||
/// Create a new texture with the specified dimensions and data.
|
||||
fn create_texture(&mut self, width: u16, height: u16, data: &[u8]) -> GameResult<Box<dyn BackendTexture>>;
|
||||
|
||||
/// Set the current blend mode.
|
||||
fn set_blend_mode(&mut self, blend: BlendMode) -> GameResult;
|
||||
|
||||
/// Set the current render target.
|
||||
fn set_render_target(&mut self, texture: Option<&Box<dyn BackendTexture>>) -> GameResult;
|
||||
|
||||
/// Draw a filled rectangle with the specified color.
|
||||
fn draw_rect(&mut self, rect: Rect, color: Color) -> GameResult;
|
||||
|
||||
/// Draw an outlined rectangle with the specified line width and color.
|
||||
fn draw_outline_rect(&mut self, rect: Rect, line_width: usize, color: Color) -> GameResult;
|
||||
|
||||
/// Set the current clipping rectangle.
|
||||
fn set_clip_rect(&mut self, rect: Option<Rect>) -> GameResult;
|
||||
|
||||
/// Get a mutable reference to the imgui context.
|
||||
fn imgui(&self) -> GameResult<&mut imgui::Context>;
|
||||
|
||||
/// Get an imgui texture id for the specified texture.
|
||||
fn imgui_texture_id(&self, texture: &Box<dyn BackendTexture>) -> GameResult<imgui::TextureId>;
|
||||
|
||||
/// Prepare the imgui context for rendering.
|
||||
fn prepare_imgui(&mut self, ui: &imgui::Ui) -> GameResult;
|
||||
|
||||
/// Render the imgui draw data.
|
||||
fn render_imgui(&mut self, draw_data: &DrawData) -> GameResult;
|
||||
|
||||
/// Draw a list of triangles, in mode similar to GL_TRIANGLES.
|
||||
fn draw_triangle_list(
|
||||
&mut self,
|
||||
vertices: &[VertexData],
|
||||
|
@ -89,18 +106,23 @@ pub trait BackendRenderer {
|
|||
}
|
||||
|
||||
pub trait BackendTexture {
|
||||
/// Get the dimensions of the texture.
|
||||
fn dimensions(&self) -> (u16, u16);
|
||||
|
||||
/// Adds a new drawing command to the texture batch.
|
||||
fn add(&mut self, command: SpriteBatchCommand);
|
||||
|
||||
/// Clear the texture batch.
|
||||
fn clear(&mut self);
|
||||
|
||||
/// Draw the texture batch to the screen.
|
||||
fn draw(&mut self) -> GameResult;
|
||||
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
}
|
||||
|
||||
pub trait BackendGamepad {
|
||||
/// Run a gamepad rumble effect using the specified parameters.
|
||||
fn set_rumble(&mut self, low_freq: u16, high_freq: u16, duration_ms: u32) -> GameResult;
|
||||
|
||||
fn instance_id(&self) -> u32;
|
||||
|
|
|
@ -406,10 +406,8 @@ impl BackendEventLoop for HorizonEventLoop {
|
|||
}
|
||||
}
|
||||
|
||||
fn new_renderer(&self, ctx: *mut Context) -> GameResult<Box<dyn BackendRenderer>> {
|
||||
fn new_renderer(&self) -> GameResult<Box<dyn BackendRenderer>> {
|
||||
let mut imgui = imgui::Context::create();
|
||||
let ctx = unsafe { &mut *ctx };
|
||||
imgui.io_mut().display_size = [ctx.screen_size.0, ctx.screen_size.1];
|
||||
imgui.fonts().build_alpha8_texture();
|
||||
|
||||
let device = DeviceMaker::new().create();
|
||||
|
|
|
@ -51,7 +51,7 @@ impl BackendEventLoop for NullEventLoop {
|
|||
}
|
||||
}
|
||||
|
||||
fn new_renderer(&self, _ctx: *mut Context) -> GameResult<Box<dyn BackendRenderer>> {
|
||||
fn new_renderer(&self) -> GameResult<Box<dyn BackendRenderer>> {
|
||||
let mut imgui = imgui::Context::create();
|
||||
imgui.io_mut().display_size = [640.0, 480.0];
|
||||
imgui.fonts().build_alpha8_texture();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use core::mem;
|
||||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::c_void;
|
||||
use std::io::Read;
|
||||
use std::ops::Deref;
|
||||
|
@ -146,7 +147,6 @@ impl WindowOrCanvas {
|
|||
struct SDL2EventLoop {
|
||||
event_pump: EventPump,
|
||||
refs: Rc<RefCell<SDL2Context>>,
|
||||
opengl_available: RefCell<bool>,
|
||||
}
|
||||
|
||||
struct SDL2Context {
|
||||
|
@ -156,6 +156,7 @@ struct SDL2Context {
|
|||
blend_mode: sdl2::render::BlendMode,
|
||||
fullscreen_type: sdl2::video::FullscreenType,
|
||||
game_controller: GameControllerSubsystem,
|
||||
preferred_renderer: Option<String>,
|
||||
}
|
||||
|
||||
impl SDL2EventLoop {
|
||||
|
@ -192,8 +193,6 @@ impl SDL2EventLoop {
|
|||
window.set_icon(icon);
|
||||
}
|
||||
|
||||
let opengl_available = if let Ok(v) = std::env::var("CAVESTORY_NO_OPENGL") { v != "1" } else { true };
|
||||
|
||||
let event_loop = SDL2EventLoop {
|
||||
event_pump,
|
||||
refs: Rc::new(RefCell::new(SDL2Context {
|
||||
|
@ -203,8 +202,8 @@ impl SDL2EventLoop {
|
|||
blend_mode: sdl2::render::BlendMode::Blend,
|
||||
fullscreen_type: sdl2::video::FullscreenType::Off,
|
||||
game_controller,
|
||||
preferred_renderer: ctx.preferred_renderer.clone(),
|
||||
})),
|
||||
opengl_available: RefCell::new(opengl_available),
|
||||
};
|
||||
|
||||
Ok(Box::new(event_loop))
|
||||
|
@ -254,6 +253,80 @@ impl SDL2EventLoop {
|
|||
|
||||
let _ = enabled;
|
||||
}
|
||||
|
||||
fn create_imgui() -> GameResult<imgui::Context> {
|
||||
let mut imgui = init_imgui()?;
|
||||
|
||||
let mut key_map = &mut imgui.io_mut().key_map;
|
||||
key_map[ImGuiKey_Backspace as usize] = Scancode::Backspace as u32;
|
||||
key_map[ImGuiKey_Delete as usize] = Scancode::Delete as u32;
|
||||
key_map[ImGuiKey_Enter as usize] = Scancode::Return as u32;
|
||||
|
||||
Ok(imgui)
|
||||
}
|
||||
|
||||
#[cfg(feature = "render-opengl")]
|
||||
fn try_create_opengl_renderer(&self) -> GameResult<Box<dyn BackendRenderer>> {
|
||||
{
|
||||
let mut refs = self.refs.borrow_mut();
|
||||
match refs.window.window().gl_create_context() {
|
||||
Ok(gl_ctx) => {
|
||||
refs.window.window().gl_make_current(&gl_ctx).map_err(|e| GameError::RenderError(e.to_string()))?;
|
||||
refs.gl_context = Some(gl_ctx);
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(GameError::RenderError(format!("Failed to initialize OpenGL context: {}", err)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SDL2GLPlatform(Rc<RefCell<SDL2Context>>);
|
||||
|
||||
impl render_opengl::GLPlatformFunctions for SDL2GLPlatform {
|
||||
fn get_proc_address(&self, name: &str) -> *const c_void {
|
||||
let refs = self.0.borrow();
|
||||
refs.video.gl_get_proc_address(name) as *const _
|
||||
}
|
||||
|
||||
fn swap_buffers(&self) {
|
||||
let mut refs = self.0.borrow();
|
||||
refs.window.window().gl_swap_window();
|
||||
}
|
||||
|
||||
fn set_swap_mode(&self, mode: SwapMode) {
|
||||
match mode {
|
||||
SwapMode::Immediate => unsafe {
|
||||
sdl2_sys::SDL_GL_SetSwapInterval(0);
|
||||
},
|
||||
SwapMode::VSync => unsafe {
|
||||
sdl2_sys::SDL_GL_SetSwapInterval(1);
|
||||
},
|
||||
SwapMode::Adaptive => unsafe {
|
||||
if sdl2_sys::SDL_GL_SetSwapInterval(-1) == -1 {
|
||||
log::warn!("Failed to enable variable refresh rate, falling back to non-V-Sync.");
|
||||
sdl2_sys::SDL_GL_SetSwapInterval(0);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let platform = Box::new(SDL2GLPlatform(self.refs.clone()));
|
||||
let gl_context: GLContext = GLContext { gles2_mode: false, platform };
|
||||
|
||||
let imgui = Self::create_imgui()?;
|
||||
|
||||
OpenGLRenderer::new(gl_context, imgui)
|
||||
}
|
||||
|
||||
fn try_create_sdl2_renderer(&self) -> GameResult<Box<dyn BackendRenderer>> {
|
||||
let imgui = Self::create_imgui()?;
|
||||
let mut refs = self.refs.borrow_mut();
|
||||
let window = std::mem::take(&mut refs.window);
|
||||
refs.window = window.make_canvas()?;
|
||||
|
||||
SDL2Renderer::new(self.refs.clone(), imgui)
|
||||
}
|
||||
}
|
||||
|
||||
impl BackendEventLoop for SDL2EventLoop {
|
||||
|
@ -440,74 +513,35 @@ impl BackendEventLoop for SDL2EventLoop {
|
|||
}
|
||||
}
|
||||
|
||||
fn new_renderer(&self, ctx: *mut Context) -> GameResult<Box<dyn BackendRenderer>> {
|
||||
#[cfg(feature = "render-opengl")]
|
||||
{
|
||||
let mut refs = self.refs.borrow_mut();
|
||||
match refs.window.window().gl_create_context() {
|
||||
Ok(gl_ctx) => {
|
||||
refs.window.window().gl_make_current(&gl_ctx).map_err(|e| GameError::RenderError(e.to_string()))?;
|
||||
refs.gl_context = Some(gl_ctx);
|
||||
}
|
||||
Err(err) => {
|
||||
*self.opengl_available.borrow_mut() = false;
|
||||
log::error!("Failed to initialize OpenGL context, falling back to SDL2 renderer: {}", err);
|
||||
}
|
||||
fn new_renderer(&self) -> GameResult<Box<dyn BackendRenderer>> {
|
||||
let mut renderers = {
|
||||
let mut renderers: Vec<(&'static str, fn(&Self) -> GameResult<Box<dyn BackendRenderer>>)> = Vec::new();
|
||||
|
||||
#[cfg(feature = "render-opengl")]
|
||||
renderers.push((OpenGLRenderer::RENDERER_ID, Self::try_create_opengl_renderer));
|
||||
|
||||
renderers.push((SDL2Renderer::RENDERER_ID, Self::try_create_sdl2_renderer));
|
||||
renderers
|
||||
};
|
||||
|
||||
if let Some(preferred_renderer) = &self.refs.borrow().preferred_renderer {
|
||||
// remove the preferred renderer from the list
|
||||
let index = renderers.iter().position(|(id, _)| id == preferred_renderer);
|
||||
let pref_renderer = index.map(|index| renderers.remove(index));
|
||||
|
||||
if let Some(pref_renderer) = pref_renderer {
|
||||
renderers.insert(0, pref_renderer);
|
||||
}
|
||||
}
|
||||
|
||||
let mut imgui = init_imgui()?;
|
||||
|
||||
#[cfg(feature = "render-opengl")]
|
||||
if *self.opengl_available.borrow() {
|
||||
let mut key_map = &mut imgui.io_mut().key_map;
|
||||
key_map[ImGuiKey_Backspace as usize] = Scancode::Backspace as u32;
|
||||
key_map[ImGuiKey_Delete as usize] = Scancode::Delete as u32;
|
||||
key_map[ImGuiKey_Enter as usize] = Scancode::Return as u32;
|
||||
|
||||
struct SDL2GLPlatform(Rc<RefCell<SDL2Context>>);
|
||||
|
||||
impl render_opengl::GLPlatformFunctions for SDL2GLPlatform {
|
||||
fn get_proc_address(&self, name: &str) -> *const c_void {
|
||||
let refs = self.0.borrow();
|
||||
refs.video.gl_get_proc_address(name) as *const _
|
||||
}
|
||||
|
||||
fn swap_buffers(&self) {
|
||||
let mut refs = self.0.borrow();
|
||||
refs.window.window().gl_swap_window();
|
||||
}
|
||||
|
||||
fn set_swap_mode(&self, mode: SwapMode) {
|
||||
match mode {
|
||||
SwapMode::Immediate => unsafe {
|
||||
sdl2_sys::SDL_GL_SetSwapInterval(0);
|
||||
},
|
||||
SwapMode::VSync => unsafe {
|
||||
sdl2_sys::SDL_GL_SetSwapInterval(1);
|
||||
},
|
||||
SwapMode::Adaptive => unsafe {
|
||||
if sdl2_sys::SDL_GL_SetSwapInterval(-1) == -1 {
|
||||
log::warn!("Failed to enable variable refresh rate, falling back to non-V-Sync.");
|
||||
sdl2_sys::SDL_GL_SetSwapInterval(0);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
for (id, renderer_fn) in renderers {
|
||||
match renderer_fn(self) {
|
||||
Ok(renderer) => return Ok(renderer),
|
||||
Err(e) => log::warn!("Failed to create renderer {}: {}", id, e),
|
||||
}
|
||||
|
||||
let platform = Box::new(SDL2GLPlatform(self.refs.clone()));
|
||||
let gl_context: GLContext = GLContext { gles2_mode: false, platform, ctx };
|
||||
|
||||
return Ok(Box::new(OpenGLRenderer::new(gl_context, imgui)));
|
||||
}
|
||||
|
||||
{
|
||||
let mut refs = self.refs.borrow_mut();
|
||||
let window = std::mem::take(&mut refs.window);
|
||||
refs.window = window.make_canvas()?;
|
||||
return Ok(Box::new(SDL2Renderer::new(self.refs.clone(), imgui)?));
|
||||
}
|
||||
Err(GameError::RenderError("Failed to create any renderer".to_owned()))
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
|
@ -563,8 +597,10 @@ struct SDL2Renderer {
|
|||
}
|
||||
|
||||
impl SDL2Renderer {
|
||||
pub const RENDERER_ID: &'static str = "sdl2";
|
||||
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new(refs: Rc<RefCell<SDL2Context>>, mut imgui: imgui::Context) -> GameResult<SDL2Renderer> {
|
||||
pub fn new(refs: Rc<RefCell<SDL2Context>>, mut imgui: imgui::Context) -> GameResult<Box<dyn BackendRenderer>> {
|
||||
imgui.set_renderer_name("SDL2Renderer".to_owned());
|
||||
let imgui_font_tex = {
|
||||
let refs = refs.clone();
|
||||
|
@ -605,7 +641,7 @@ impl SDL2Renderer {
|
|||
};
|
||||
imgui.fonts().tex_id = TextureId::new(imgui_font_tex.texture.as_ref().unwrap().raw() as usize);
|
||||
|
||||
Ok((SDL2Renderer { refs, imgui: Rc::new(RefCell::new(imgui)), imgui_font_tex }))
|
||||
Ok(Box::new(SDL2Renderer { refs, imgui: Rc::new(RefCell::new(imgui)), imgui_font_tex }))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -293,48 +293,10 @@ impl BackendEventLoop for WinitEventLoop {
|
|||
event_loop.run_app(self);
|
||||
}
|
||||
|
||||
fn new_renderer(&self, ctx: *mut Context) -> GameResult<Box<dyn BackendRenderer>> {
|
||||
fn new_renderer(&self) -> GameResult<Box<dyn BackendRenderer>> {
|
||||
let mut imgui = init_imgui()?;
|
||||
imgui.io_mut().display_size = [640.0, 480.0];
|
||||
|
||||
// let refs = self.refs.clone();
|
||||
// let user_data = Rc::into_raw(refs) as *mut c_void;
|
||||
|
||||
// unsafe fn get_proc_address(user_data: &mut *mut c_void, name: &str) -> *const c_void {
|
||||
// let refs = Rc::from_raw(*user_data as *mut UnsafeCell<Option<WindowedContext<PossiblyCurrent>>>);
|
||||
|
||||
// let result = {
|
||||
// let refs = &mut *refs.get();
|
||||
|
||||
// if let Some(refs) = refs {
|
||||
// refs.get_proc_address(name)
|
||||
// } else {
|
||||
// std::ptr::null()
|
||||
// }
|
||||
// };
|
||||
|
||||
// *user_data = Rc::into_raw(refs) as *mut c_void;
|
||||
|
||||
// result
|
||||
// }
|
||||
|
||||
// unsafe fn swap_buffers(user_data: &mut *mut c_void) {
|
||||
// let refs = Rc::from_raw(*user_data as *mut UnsafeCell<Option<WindowedContext<PossiblyCurrent>>>);
|
||||
|
||||
// {
|
||||
// let refs = &mut *refs.get();
|
||||
|
||||
// if let Some(refs) = refs {
|
||||
// refs.swap_buffers();
|
||||
// }
|
||||
// }
|
||||
|
||||
// *user_data = Rc::into_raw(refs) as *mut c_void;
|
||||
// }
|
||||
|
||||
// let gl_context = GLContext { gles2_mode: true, is_sdl: false, get_proc_address, swap_buffers, user_data, ctx };
|
||||
|
||||
// Ok(Box::new(OpenGLRenderer::new(gl_context, imgui)))
|
||||
Ok(Box::new(super::backend_null::NullRenderer::new(imgui)))
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ pub struct Context {
|
|||
pub shutdown_requested: bool,
|
||||
pub size_hint: (u16, u16),
|
||||
pub window_title: String,
|
||||
pub preferred_renderer: Option<String>,
|
||||
pub(crate) filesystem: Filesystem,
|
||||
pub(crate) renderer: Option<Box<dyn BackendRenderer>>,
|
||||
pub(crate) gamepad_context: GamepadContext,
|
||||
|
@ -29,6 +30,7 @@ impl Context {
|
|||
shutdown_requested: false,
|
||||
size_hint: (640, 480),
|
||||
window_title: "Game".to_string(),
|
||||
preferred_renderer: None,
|
||||
filesystem: Filesystem::new(),
|
||||
renderer: None,
|
||||
gamepad_context: GamepadContext::new(),
|
||||
|
@ -43,7 +45,7 @@ impl Context {
|
|||
pub fn run(&mut self, game: &mut Game) -> GameResult {
|
||||
let backend = init_backend(self.headless, self.size_hint)?;
|
||||
let mut event_loop = backend.create_event_loop(self)?;
|
||||
self.renderer = Some(event_loop.new_renderer(self as *mut Context)?);
|
||||
self.renderer = Some(event_loop.new_renderer()?);
|
||||
|
||||
event_loop.run(game, self);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue