1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2025-07-28 16:21:32 +00:00

Compare commits

...

3 commits

Author SHA1 Message Date
Alula 426f3ce4d0
Update itertools and num-* crates 2024-08-28 15:01:15 +02:00
Alula 86747e0230
Don't do unsafe stupidity to handle imgui::Context 2024-08-28 15:00:32 +02:00
Alula fb87b3d5ab
Refactor renderer initialization 2024-08-28 12:37:57 +02:00
10 changed files with 671 additions and 642 deletions

View file

@ -51,7 +51,7 @@ exe = []
android = [] android = []
[dependencies] [dependencies]
glutin = { path = "./3rdparty/glutin/glutin", optional = true } #glutin = { path = "./3rdparty/glutin/glutin", optional = true }
#winit = { path = "./3rdparty/winit", optional = true, default_features = false, features = ["x11"] } #winit = { path = "./3rdparty/winit", optional = true, default_features = false, features = ["x11"] }
#sdl2 = { path = "./3rdparty/rust-sdl2", optional = true, features = ["unsafe_textures", "bundled", "static-link"] } #sdl2 = { path = "./3rdparty/rust-sdl2", optional = true, features = ["unsafe_textures", "bundled", "static-link"] }
#sdl2-sys = { path = "./3rdparty/rust-sdl2/sdl2-sys", optional = true, features = ["bundled", "static-link"] } #sdl2-sys = { path = "./3rdparty/rust-sdl2/sdl2-sys", optional = true, features = ["bundled", "static-link"] }
@ -66,15 +66,15 @@ discord-rich-presence = { version = "0.2", optional = true }
downcast = "0.11" downcast = "0.11"
encoding_rs = "0.8.33" encoding_rs = "0.8.33"
fern = "0.6.2" fern = "0.6.2"
#glutin = { git = "https://github.com/doukutsu-rs/glutin.git", rev = "2dd95f042e6e090d36f577cbea125560dd99bd27", optional = true, default_features = false, features = ["x11"] } glutin = { version = "0.32.0", optional = true }
imgui = { git = "https://github.com/imgui-rs/imgui-rs.git", rev = "67f7f11363e62f09aa0e1288a17800e505860486" } imgui = { git = "https://github.com/imgui-rs/imgui-rs.git", rev = "67f7f11363e62f09aa0e1288a17800e505860486" }
image = { version = "0.24", default-features = false, features = ["png", "bmp"] } image = { version = "0.24", default-features = false, features = ["png", "bmp"] }
itertools = "0.10" itertools = "0.13.0"
include-flate = "0.3.0" include-flate = "0.3.0"
lazy_static = "1.4" lazy_static = "1.4"
lewton = { version = "0.10", optional = true } lewton = { version = "0.10", optional = true }
log = "0.4" log = "0.4"
num-derive = "0.3" num-derive = "0.4"
num-traits = "0.2" num-traits = "0.2"
open = "3.2" open = "3.2"
paste = "1.0" paste = "1.0"

View file

@ -1,4 +1,5 @@
use std::any::Any; use std::any::Any;
use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use imgui::DrawData; use imgui::DrawData;
@ -36,48 +37,65 @@ pub trait Backend {
pub trait BackendEventLoop { pub trait BackendEventLoop {
fn run(&mut self, game: &mut Game, ctx: &mut Context); 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; fn as_any(&self) -> &dyn Any;
} }
pub trait BackendRenderer { pub trait BackendRenderer {
/// Human-readable name for the renderer. May return different values based on current platform or settings.
fn renderer_name(&self) -> String; fn renderer_name(&self) -> String;
/// Clear the current render target with the specified color.
fn clear(&mut self, color: Color); fn clear(&mut self, color: Color);
/// Present the current frame to the screen.
fn present(&mut self) -> GameResult; fn present(&mut self) -> GameResult;
/// Sets the preferred frame swap mode.
fn set_swap_mode(&mut self, _mode: SwapMode) -> GameResult { fn set_swap_mode(&mut self, _mode: SwapMode) -> GameResult {
Ok(()) Ok(())
} }
// Prepare the renderer for drawing.
fn prepare_draw(&mut self, _width: f32, _height: f32) -> GameResult { fn prepare_draw(&mut self, _width: f32, _height: f32) -> GameResult {
Ok(()) Ok(())
} }
/// Create a new mutable texture with the specified dimensions.
fn create_texture_mutable(&mut self, width: u16, height: u16) -> GameResult<Box<dyn BackendTexture>>; 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>>; 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; 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; 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; 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; 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; fn set_clip_rect(&mut self, rect: Option<Rect>) -> GameResult;
fn imgui(&self) -> GameResult<&mut imgui::Context>; /// Get a reference to the imgui context.
fn imgui(&self) -> GameResult<Rc<RefCell<imgui::Context>>>;
/// Get an imgui texture id for the specified texture.
fn imgui_texture_id(&self, texture: &Box<dyn BackendTexture>) -> GameResult<imgui::TextureId>; 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; fn prepare_imgui(&mut self, ui: &imgui::Ui) -> GameResult;
/// Render the imgui draw data.
fn render_imgui(&mut self, draw_data: &DrawData) -> GameResult; fn render_imgui(&mut self, draw_data: &DrawData) -> GameResult;
/// Draw a list of triangles, in mode similar to GL_TRIANGLES.
fn draw_triangle_list( fn draw_triangle_list(
&mut self, &mut self,
vertices: &[VertexData], vertices: &[VertexData],
@ -89,18 +107,23 @@ pub trait BackendRenderer {
} }
pub trait BackendTexture { pub trait BackendTexture {
/// Get the dimensions of the texture.
fn dimensions(&self) -> (u16, u16); fn dimensions(&self) -> (u16, u16);
/// Adds a new drawing command to the texture batch.
fn add(&mut self, command: SpriteBatchCommand); fn add(&mut self, command: SpriteBatchCommand);
/// Clear the texture batch.
fn clear(&mut self); fn clear(&mut self);
/// Draw the texture batch to the screen.
fn draw(&mut self) -> GameResult; fn draw(&mut self) -> GameResult;
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
} }
pub trait BackendGamepad { 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 set_rumble(&mut self, low_freq: u16, high_freq: u16, duration_ms: u32) -> GameResult;
fn instance_id(&self) -> u32; fn instance_id(&self) -> u32;

View file

@ -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 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(); imgui.fonts().build_alpha8_texture();
let device = DeviceMaker::new().create(); let device = DeviceMaker::new().create();

View file

@ -1,6 +1,7 @@
use std::any::Any; use std::any::Any;
use std::cell::RefCell; use std::cell::RefCell;
use std::mem; use std::mem;
use std::rc::Rc;
use imgui::{DrawData, TextureId, Ui}; use imgui::{DrawData, TextureId, Ui};
@ -25,7 +26,7 @@ impl Backend for NullBackend {
fn create_event_loop(&self, _ctx: &Context) -> GameResult<Box<dyn BackendEventLoop>> { fn create_event_loop(&self, _ctx: &Context) -> GameResult<Box<dyn BackendEventLoop>> {
Ok(Box::new(NullEventLoop)) Ok(Box::new(NullEventLoop))
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
@ -51,12 +52,9 @@ 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(); let mut imgui = imgui::Context::create();
imgui.io_mut().display_size = [640.0, 480.0]; NullRenderer::new(imgui)
imgui.fonts().build_alpha8_texture();
Ok(Box::new(NullRenderer(RefCell::new(imgui))))
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
@ -84,12 +82,12 @@ impl BackendTexture for NullTexture {
} }
} }
pub struct NullRenderer(RefCell<imgui::Context>); pub struct NullRenderer(Rc<RefCell<imgui::Context>>);
impl NullRenderer { impl NullRenderer {
pub fn new(mut imgui: imgui::Context) -> Self { pub fn new(mut imgui: imgui::Context) -> GameResult<Box<dyn BackendRenderer>> {
let _ = imgui.fonts().build_alpha8_texture(); let _ = imgui.fonts().build_alpha8_texture();
NullRenderer(RefCell::new(imgui)) Ok(Box::new(NullRenderer(Rc::new(RefCell::new(imgui)))))
} }
} }
@ -132,8 +130,8 @@ impl BackendRenderer for NullRenderer {
Ok(()) Ok(())
} }
fn imgui(&self) -> GameResult<&mut imgui::Context> { fn imgui(&self) -> GameResult<Rc<RefCell<imgui::Context>>> {
unsafe { Ok(&mut *self.0.as_ptr()) } Ok(self.0.clone())
} }
fn imgui_texture_id(&self, _texture: &Box<dyn BackendTexture>) -> GameResult<TextureId> { fn imgui_texture_id(&self, _texture: &Box<dyn BackendTexture>) -> GameResult<TextureId> {

View file

@ -1,6 +1,7 @@
use core::mem; use core::mem;
use std::any::Any; use std::any::Any;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::c_void; use std::ffi::c_void;
use std::io::Read; use std::io::Read;
use std::ops::Deref; use std::ops::Deref;
@ -43,6 +44,8 @@ use crate::game::shared_game_state::WindowMode;
use crate::game::Game; use crate::game::Game;
use crate::game::GAME_SUSPENDED; use crate::game::GAME_SUSPENDED;
use super::graphics;
pub struct SDL2Backend { pub struct SDL2Backend {
context: Sdl, context: Sdl,
size_hint: (u16, u16), size_hint: (u16, u16),
@ -146,7 +149,6 @@ impl WindowOrCanvas {
struct SDL2EventLoop { struct SDL2EventLoop {
event_pump: EventPump, event_pump: EventPump,
refs: Rc<RefCell<SDL2Context>>, refs: Rc<RefCell<SDL2Context>>,
opengl_available: RefCell<bool>,
} }
struct SDL2Context { struct SDL2Context {
@ -156,6 +158,7 @@ struct SDL2Context {
blend_mode: sdl2::render::BlendMode, blend_mode: sdl2::render::BlendMode,
fullscreen_type: sdl2::video::FullscreenType, fullscreen_type: sdl2::video::FullscreenType,
game_controller: GameControllerSubsystem, game_controller: GameControllerSubsystem,
preferred_renderer: Option<String>,
} }
impl SDL2EventLoop { impl SDL2EventLoop {
@ -192,8 +195,6 @@ impl SDL2EventLoop {
window.set_icon(icon); 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 { let event_loop = SDL2EventLoop {
event_pump, event_pump,
refs: Rc::new(RefCell::new(SDL2Context { refs: Rc::new(RefCell::new(SDL2Context {
@ -203,8 +204,8 @@ impl SDL2EventLoop {
blend_mode: sdl2::render::BlendMode::Blend, blend_mode: sdl2::render::BlendMode::Blend,
fullscreen_type: sdl2::video::FullscreenType::Off, fullscreen_type: sdl2::video::FullscreenType::Off,
game_controller, game_controller,
preferred_renderer: ctx.preferred_renderer.clone(),
})), })),
opengl_available: RefCell::new(opengl_available),
}; };
Ok(Box::new(event_loop)) Ok(Box::new(event_loop))
@ -254,25 +255,101 @@ impl SDL2EventLoop {
let _ = enabled; 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()?;
fn try_create_sdl2_renderer(&self) -> GameResult<Box<dyn BackendRenderer>> {
let mut refs = self.refs.borrow_mut();
let window = std::mem::take(&mut refs.window);
refs.window = window.make_canvas()?;
let imgui = Self::create_imgui()?;
SDL2Renderer::new(self.refs.clone(), imgui)
}
} }
impl BackendEventLoop for SDL2EventLoop { impl BackendEventLoop for SDL2EventLoop {
fn run(&mut self, game: &mut Game, ctx: &mut Context) { fn run(&mut self, game: &mut Game, ctx: &mut Context) {
let imgui = unsafe { (&*(ctx.renderer.as_ref().unwrap() as *const Box<dyn BackendRenderer>)).imgui().unwrap() }; let imgui = graphics::imgui_context(ctx).unwrap();
let mut imgui_sdl2 = ImguiSdl2::new(imgui, self.refs.deref().borrow().window.window()); let mut imgui_sdl2 = ImguiSdl2::new(&mut imgui.borrow_mut(), self.refs.deref().borrow().window.window());
{ {
let state = game.state.get_mut(); let state = game.state.get_mut();
let (width, height) = self.refs.deref().borrow().window.window().size(); let (width, height) = self.refs.deref().borrow().window.window().size();
ctx.screen_size = (width.max(1) as f32, height.max(1) as f32); ctx.screen_size = (width.max(1) as f32, height.max(1) as f32);
imgui.io_mut().display_size = [ctx.screen_size.0, ctx.screen_size.1]; imgui.borrow_mut().io_mut().display_size = [ctx.screen_size.0, ctx.screen_size.1];
let _ = state.handle_resize(ctx); let _ = state.handle_resize(ctx);
} }
loop { loop {
for event in self.event_pump.poll_iter() { for event in self.event_pump.poll_iter() {
imgui_sdl2.handle_event(imgui, &event); imgui_sdl2.handle_event(&mut imgui.borrow_mut(), &event);
match event { match event {
Event::Quit { .. } => { Event::Quit { .. } => {
@ -304,11 +381,7 @@ impl BackendEventLoop for SDL2EventLoop {
let state = game.state.get_mut(); let state = game.state.get_mut();
ctx.screen_size = (width.max(1) as f32, height.max(1) as f32); ctx.screen_size = (width.max(1) as f32, height.max(1) as f32);
if let Some(renderer) = &ctx.renderer { imgui.borrow_mut().io_mut().display_size = [ctx.screen_size.0, ctx.screen_size.1];
if let Ok(imgui) = renderer.imgui() {
imgui.io_mut().display_size = [ctx.screen_size.0, ctx.screen_size.1];
}
}
state.handle_resize(ctx).unwrap(); state.handle_resize(ctx).unwrap();
} }
_ => {} _ => {}
@ -431,7 +504,7 @@ impl BackendEventLoop for SDL2EventLoop {
game.update(ctx).unwrap(); game.update(ctx).unwrap();
imgui_sdl2.prepare_frame( imgui_sdl2.prepare_frame(
imgui.io_mut(), imgui.borrow_mut().io_mut(),
self.refs.deref().borrow().window.window(), self.refs.deref().borrow().window.window(),
&self.event_pump.mouse_state(), &self.event_pump.mouse_state(),
); );
@ -440,74 +513,35 @@ impl BackendEventLoop for SDL2EventLoop {
} }
} }
fn new_renderer(&self, ctx: *mut Context) -> GameResult<Box<dyn BackendRenderer>> { fn new_renderer(&self) -> GameResult<Box<dyn BackendRenderer>> {
#[cfg(feature = "render-opengl")] let mut renderers = {
{ let mut renderers: Vec<(&'static str, fn(&Self) -> GameResult<Box<dyn BackendRenderer>>)> = Vec::new();
let mut refs = self.refs.borrow_mut();
match refs.window.window().gl_create_context() { #[cfg(feature = "render-opengl")]
Ok(gl_ctx) => { renderers.push((OpenGLRenderer::RENDERER_ID, Self::try_create_opengl_renderer));
refs.window.window().gl_make_current(&gl_ctx).map_err(|e| GameError::RenderError(e.to_string()))?;
refs.gl_context = Some(gl_ctx); renderers.push((SDL2Renderer::RENDERER_ID, Self::try_create_sdl2_renderer));
} renderers
Err(err) => { };
*self.opengl_available.borrow_mut() = false;
log::error!("Failed to initialize OpenGL context, falling back to SDL2 renderer: {}", err); 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()?; for (id, renderer_fn) in renderers {
match renderer_fn(self) {
#[cfg(feature = "render-opengl")] Ok(renderer) => return Ok(renderer),
if *self.opengl_available.borrow() { Err(e) => log::warn!("Failed to create renderer {}: {}", id, e),
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);
}
},
}
}
} }
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)));
} }
{ Err(GameError::RenderError("Failed to create any renderer".to_owned()))
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)?));
}
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
@ -563,8 +597,10 @@ struct SDL2Renderer {
} }
impl SDL2Renderer { impl SDL2Renderer {
pub const RENDERER_ID: &'static str = "sdl2";
#[allow(clippy::new_ret_no_self)] #[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()); imgui.set_renderer_name("SDL2Renderer".to_owned());
let imgui_font_tex = { let imgui_font_tex = {
let refs = refs.clone(); 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); 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 }))
} }
} }
@ -843,8 +879,8 @@ impl BackendRenderer for SDL2Renderer {
Ok(()) Ok(())
} }
fn imgui(&self) -> GameResult<&mut imgui::Context> { fn imgui(&self) -> GameResult<Rc<RefCell<imgui::Context>>> {
unsafe { Ok(&mut *self.imgui.as_ptr()) } Ok(self.imgui.clone())
} }
fn imgui_texture_id(&self, texture: &Box<dyn BackendTexture>) -> GameResult<TextureId> { fn imgui_texture_id(&self, texture: &Box<dyn BackendTexture>) -> GameResult<TextureId> {

View file

@ -293,48 +293,10 @@ impl BackendEventLoop for WinitEventLoop {
event_loop.run_app(self); 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()?; let mut imgui = init_imgui()?;
imgui.io_mut().display_size = [640.0, 480.0]; 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))) Ok(Box::new(super::backend_null::NullRenderer::new(imgui)))
} }

View file

@ -12,6 +12,7 @@ pub struct Context {
pub shutdown_requested: bool, pub shutdown_requested: bool,
pub size_hint: (u16, u16), pub size_hint: (u16, u16),
pub window_title: String, pub window_title: String,
pub preferred_renderer: Option<String>,
pub(crate) filesystem: Filesystem, pub(crate) filesystem: Filesystem,
pub(crate) renderer: Option<Box<dyn BackendRenderer>>, pub(crate) renderer: Option<Box<dyn BackendRenderer>>,
pub(crate) gamepad_context: GamepadContext, pub(crate) gamepad_context: GamepadContext,
@ -29,6 +30,7 @@ impl Context {
shutdown_requested: false, shutdown_requested: false,
size_hint: (640, 480), size_hint: (640, 480),
window_title: "Game".to_string(), window_title: "Game".to_string(),
preferred_renderer: None,
filesystem: Filesystem::new(), filesystem: Filesystem::new(),
renderer: None, renderer: None,
gamepad_context: GamepadContext::new(), gamepad_context: GamepadContext::new(),
@ -43,7 +45,7 @@ impl Context {
pub fn run(&mut self, game: &mut Game) -> GameResult { pub fn run(&mut self, game: &mut Game) -> GameResult {
let backend = init_backend(self.headless, self.size_hint)?; let backend = init_backend(self.headless, self.size_hint)?;
let mut event_loop = backend.create_event_loop(self)?; 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); event_loop.run(game, self);

View file

@ -1,3 +1,6 @@
use std::cell::RefCell;
use std::rc::Rc;
use crate::common::{Color, Rect}; use crate::common::{Color, Rect};
use crate::framework::backend::{BackendShader, BackendTexture, VertexData}; use crate::framework::backend::{BackendShader, BackendTexture, VertexData};
use crate::framework::context::Context; use crate::framework::context::Context;
@ -130,7 +133,7 @@ pub fn set_clip_rect(ctx: &mut Context, rect: Option<Rect>) -> GameResult {
Err(GameError::RenderError("Rendering backend hasn't been initialized yet.".to_string())) Err(GameError::RenderError("Rendering backend hasn't been initialized yet.".to_string()))
} }
pub fn imgui_context(ctx: &Context) -> GameResult<&mut imgui::Context> { pub fn imgui_context(ctx: &Context) -> GameResult<Rc<RefCell<imgui::Context>>> {
if let Some(renderer) = ctx.renderer.as_ref() { if let Some(renderer) = ctx.renderer.as_ref() {
return renderer.imgui(); return renderer.imgui();
} }

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
use std::time::Instant; use std::time::Instant;
use imgui::{FontConfig, FontSource};
use imgui::sys::*; use imgui::sys::*;
use imgui::{FontConfig, FontSource};
use crate::framework::context::Context; use crate::framework::context::Context;
use crate::framework::error::GameResult; use crate::framework::error::GameResult;
@ -108,19 +108,20 @@ impl UI {
} }
pub fn draw(&mut self, state: &mut SharedGameState, ctx: &mut Context, scene: &mut Box<dyn Scene>) -> GameResult { pub fn draw(&mut self, state: &mut SharedGameState, ctx: &mut Context, scene: &mut Box<dyn Scene>) -> GameResult {
let ctx2 = unsafe { &mut *(ctx as *const Context as *mut Context) };
let imgui = imgui_context(ctx)?; let imgui = imgui_context(ctx)?;
let mut imgui = imgui.borrow_mut();
let now = Instant::now(); let now = Instant::now();
imgui.io_mut().update_delta_time(now - self.last_frame); imgui.io_mut().update_delta_time(now - self.last_frame);
self.last_frame = now; self.last_frame = now;
let mut ui = imgui.new_frame(); let mut ui = imgui.new_frame();
scene.imgui_draw(&mut self.components, state, ctx2, &mut ui)?; scene.imgui_draw(&mut self.components, state, ctx, &mut ui)?;
prepare_imgui(ctx2, &ui); prepare_imgui(ctx, &ui);
let draw_data = imgui.render(); let draw_data = imgui.render();
render_imgui(ctx2, draw_data)?; render_imgui(ctx, draw_data)?;
Ok(()) Ok(())
} }