From e2afafdfa371e8f581bfa7401fe68634a0b8c87e Mon Sep 17 00:00:00 2001 From: Alula <6276139+alula@users.noreply.github.com> Date: Wed, 5 Jan 2022 11:42:39 +0100 Subject: [PATCH] graphics::imgui_texture_id and soundness fixes --- src/framework/backend.rs | 14 ++++- src/framework/backend_null.rs | 11 +++- src/framework/backend_sdl2.rs | 97 ++++++++++++++++++++-------------- src/framework/graphics.rs | 8 +++ src/framework/render_opengl.rs | 75 ++++++++++++++++---------- 5 files changed, 136 insertions(+), 69 deletions(-) diff --git a/src/framework/backend.rs b/src/framework/backend.rs index 72521f9..91d3761 100644 --- a/src/framework/backend.rs +++ b/src/framework/backend.rs @@ -1,3 +1,4 @@ +use std::any::Any; use imgui::DrawData; use crate::common::{Color, Rect}; @@ -17,7 +18,7 @@ pub struct VertexData { #[derive(Copy, Clone, PartialEq)] pub enum BackendShader { Fill, - Texture + Texture, } pub trait Backend { @@ -57,13 +58,20 @@ pub trait BackendRenderer { fn imgui(&self) -> GameResult<&mut imgui::Context>; + fn imgui_texture_id(&self, texture: &Box) -> GameResult; + fn render_imgui(&mut self, draw_data: &DrawData) -> GameResult; fn supports_vertex_draw(&self) -> bool { false } - fn draw_triangle_list(&mut self, vertices: Vec, texture: Option<&Box>, shader: BackendShader) -> GameResult; + fn draw_triangle_list( + &mut self, + vertices: Vec, + texture: Option<&Box>, + shader: BackendShader, + ) -> GameResult; } pub trait BackendTexture { @@ -74,6 +82,8 @@ pub trait BackendTexture { fn clear(&mut self); fn draw(&mut self) -> GameResult; + + fn as_any(&self) -> &dyn Any; } #[allow(unreachable_code)] diff --git a/src/framework/backend_null.rs b/src/framework/backend_null.rs index 04f199f..eed488f 100644 --- a/src/framework/backend_null.rs +++ b/src/framework/backend_null.rs @@ -1,7 +1,8 @@ +use std::any::Any; use std::cell::RefCell; use std::mem; -use imgui::DrawData; +use imgui::{DrawData, TextureId}; use crate::common::{Color, Rect}; use crate::framework::backend::{ @@ -79,6 +80,10 @@ impl BackendTexture for NullTexture { fn draw(&mut self) -> GameResult<()> { Ok(()) } + + fn as_any(&self) -> &dyn Any { + self + } } pub struct NullRenderer(RefCell); @@ -126,6 +131,10 @@ impl BackendRenderer for NullRenderer { unsafe { Ok(&mut *self.0.as_ptr()) } } + fn imgui_texture_id(&self, _texture: &Box) -> GameResult { + Ok(TextureId::from(0)) + } + fn render_imgui(&mut self, _draw_data: &DrawData) -> GameResult { Ok(()) } diff --git a/src/framework/backend_sdl2.rs b/src/framework/backend_sdl2.rs index 31b68cf..3e9bc3a 100644 --- a/src/framework/backend_sdl2.rs +++ b/src/framework/backend_sdl2.rs @@ -1,7 +1,10 @@ use core::mem; +use std::any::Any; +use std::borrow::Borrow; use std::cell::{RefCell, UnsafeCell}; -use std::collections::HashMap; use std::ffi::c_void; +use std::ops::Deref; +use std::ptr::null_mut; use std::rc::Rc; use std::time::Duration; @@ -11,7 +14,7 @@ use sdl2::event::{Event, WindowEvent}; use sdl2::keyboard::Scancode; use sdl2::mouse::{Cursor, SystemCursor}; use sdl2::pixels::PixelFormatEnum; -use sdl2::render::{Texture, TextureCreator, WindowCanvas}; +use sdl2::render::{Texture, TextureCreator, TextureQuery, WindowCanvas}; use sdl2::video::GLProfile; use sdl2::video::WindowContext; use sdl2::{keyboard, pixels, EventPump, Sdl, VideoSubsystem}; @@ -27,6 +30,7 @@ use crate::framework::keyboard::ScanCode; use crate::framework::render_opengl::{GLContext, OpenGLRenderer}; use crate::framework::ui::init_imgui; use crate::Game; +use crate::GameError::RenderError; use crate::GAME_SUSPENDED; pub struct SDL2Backend { @@ -119,7 +123,7 @@ impl BackendEventLoop for SDL2EventLoop { }; { - let (width, height) = self.refs.borrow().canvas.window().size(); + let (width, height) = self.refs.deref().borrow().canvas.window().size(); 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]; @@ -230,7 +234,7 @@ impl BackendEventLoop for SDL2EventLoop { imgui_sdl2.prepare_frame( imgui.io_mut(), - self.refs.borrow().canvas.window(), + self.refs.deref().borrow().canvas.window(), &self.event_pump.mouse_state(), ); game.draw(ctx).unwrap(); @@ -300,20 +304,18 @@ struct SDL2Renderer { refs: Rc>, imgui: Rc>, imgui_event: Rc>, - imgui_textures: HashMap, + imgui_font_tex: SDL2Texture, } impl SDL2Renderer { #[allow(clippy::new_ret_no_self)] pub fn new(refs: Rc>) -> GameResult> { let mut imgui = init_imgui()?; - let mut imgui_textures = HashMap::new(); imgui.set_renderer_name("SDL2Renderer".to_owned()); - { + let imgui_font_tex = { let refs = refs.clone(); let mut fonts = imgui.fonts(); - let id = fonts.tex_id; let font_tex = fonts.build_rgba32_texture(); let mut texture = refs @@ -339,17 +341,15 @@ impl SDL2Renderer { }) .map_err(|e| GameError::RenderError(e.to_string()))?; - imgui_textures.insert( - id, - SDL2Texture { - refs: refs.clone(), - texture: Some(texture), - width: font_tex.width as u16, - height: font_tex.height as u16, - commands: vec![], - }, - ); - } + SDL2Texture { + refs: refs.clone(), + texture: Some(texture), + width: font_tex.width as u16, + height: font_tex.height as u16, + commands: vec![], + } + }; + imgui.fonts().tex_id = TextureId::new(imgui_font_tex.texture.as_ref().unwrap().raw() as usize); let imgui_sdl2 = unsafe { let refs = &mut *refs.as_ptr(); @@ -360,7 +360,7 @@ impl SDL2Renderer { refs, imgui: Rc::new(RefCell::new(imgui)), imgui_event: Rc::new(RefCell::new(imgui_sdl2)), - imgui_textures, + imgui_font_tex, })) } } @@ -426,7 +426,6 @@ impl BackendRenderer for SDL2Renderer { let mut refs = self.refs.borrow_mut(); refs.canvas.set_clip_rect(Some(sdl2::rect::Rect::new(0, 0, width as u32, height as u32))); - //refs.canvas.set_clip_rect(None); Ok(()) } @@ -483,19 +482,23 @@ impl BackendRenderer for SDL2Renderer { } fn set_render_target(&mut self, texture: Option<&Box>) -> GameResult { - let renderer = self.refs.borrow().canvas.raw(); + let renderer = self.refs.deref().borrow().canvas.raw(); - // todo: horribly unsafe match texture { - Some(texture) => unsafe { - let sdl2_texture: &Box = std::mem::transmute(texture); + Some(texture) => { + let sdl2_texture = texture + .as_any() + .downcast_ref::() + .ok_or(RenderError("This texture was not created by OpenGL backend.".to_string()))?; - if let Some(target) = sdl2_texture.texture.as_ref() { - set_raw_target(renderer, target.raw())?; - } else { - set_raw_target(renderer, std::ptr::null_mut())?; + unsafe { + if let Some(target) = sdl2_texture.texture.as_ref() { + set_raw_target(renderer, target.raw())?; + } else { + set_raw_target(renderer, std::ptr::null_mut())?; + } } - }, + } None => unsafe { set_raw_target(renderer, std::ptr::null_mut())?; }, @@ -591,6 +594,15 @@ impl BackendRenderer for SDL2Renderer { unsafe { Ok(&mut *self.imgui.as_ptr()) } } + fn imgui_texture_id(&self, texture: &Box) -> GameResult { + let sdl_texture = texture + .as_any() + .downcast_ref::() + .ok_or(GameError::RenderError("This texture was not created by SDL backend.".to_string()))?; + + Ok(TextureId::new(sdl_texture.texture.as_ref().map(|t| t.raw()).unwrap_or(null_mut()) as usize)) + } + fn render_imgui(&mut self, draw_data: &DrawData) -> GameResult { let mut refs = self.refs.borrow_mut(); @@ -668,13 +680,17 @@ impl BackendRenderer for SDL2Renderer { } } - if let Some(surf) = self.imgui_textures.get_mut(&cmd_params.texture_id) { + let ptr = cmd_params.texture_id.id() as *mut sdl2::sys::SDL_Texture; + if !ptr.is_null() { + let mut surf = unsafe { refs.texture_creator.raw_create_texture(ptr) }; + let TextureQuery { width, height, .. } = surf.query(); + if is_rect { let src = sdl2::rect::Rect::new( - (tex_pos[0] * surf.width as f32) as i32, - (tex_pos[1] * surf.height as f32) as i32, - ((tex_pos[2] - tex_pos[0]) * surf.width as f32) as u32, - ((tex_pos[3] - tex_pos[1]) * surf.height as f32) as u32, + (tex_pos[0] * width as f32) as i32, + (tex_pos[1] * height as f32) as i32, + ((tex_pos[2] - tex_pos[0]) * width as f32) as u32, + ((tex_pos[3] - tex_pos[1]) * height as f32) as u32, ); let dest = sdl2::rect::Rect::new( min[0] as i32, @@ -683,12 +699,11 @@ impl BackendRenderer for SDL2Renderer { (max[1] - min[1]) as u32, ); - let tex = surf.texture.as_mut().unwrap(); - tex.set_color_mod(v1.col[0], v1.col[1], v1.col[2]); - tex.set_alpha_mod(v1.col[3]); + surf.set_color_mod(v1.col[0], v1.col[1], v1.col[2]); + surf.set_alpha_mod(v1.col[3]); refs.canvas - .copy(tex, src, dest) + .copy(&surf, src, dest) .map_err(|e| GameError::RenderError(e.to_string()))?; } else { /*sdl2::sys::gfx::primitives::filledPolygonRGBA( @@ -835,6 +850,10 @@ impl BackendTexture for SDL2Texture { } } } + + fn as_any(&self) -> &dyn Any { + self + } } impl Drop for SDL2Texture { diff --git a/src/framework/graphics.rs b/src/framework/graphics.rs index a6a2927..f1bee30 100644 --- a/src/framework/graphics.rs +++ b/src/framework/graphics.rs @@ -120,6 +120,14 @@ pub fn imgui_context(ctx: &Context) -> GameResult<&mut imgui::Context> { Err(GameError::RenderError("Rendering backend hasn't been initialized yet.".to_string())) } +pub fn imgui_texture_id(ctx: &Context, texture: &Box) -> GameResult { + if let Some(renderer) = ctx.renderer.as_ref() { + return renderer.imgui_texture_id(texture); + } + + Err(GameError::RenderError("Rendering backend hasn't been initialized yet.".to_string())) +} + pub fn render_imgui(ctx: &mut Context, draw_data: &imgui::DrawData) -> GameResult { if let Some(renderer) = ctx.renderer.as_mut() { return renderer.render_imgui(draw_data); diff --git a/src/framework/render_opengl.rs b/src/framework/render_opengl.rs index 4f4c0a5..ff254a0 100644 --- a/src/framework/render_opengl.rs +++ b/src/framework/render_opengl.rs @@ -1,3 +1,4 @@ +use std::any::Any; use std::cell::{RefCell, UnsafeCell}; use std::ffi::{c_void, CStr}; use std::mem; @@ -5,7 +6,7 @@ use std::mem::MaybeUninit; use std::ptr::null; use std::sync::Arc; -use imgui::{DrawCmd, DrawCmdParams, DrawData, DrawIdx, DrawVert}; +use imgui::{DrawCmd, DrawCmdParams, DrawData, DrawIdx, DrawVert, TextureId}; use crate::common::{Color, Rect}; use crate::framework::backend::{BackendRenderer, BackendShader, BackendTexture, SpriteBatchCommand, VertexData}; @@ -232,6 +233,10 @@ impl BackendTexture for OpenGLTexture { } } } + + fn as_any(&self) -> &dyn Any { + self + } } impl Drop for OpenGLTexture { @@ -882,7 +887,10 @@ impl BackendRenderer for OpenGLRenderer { if let Some((_, gl)) = self.get_context() { unsafe { if let Some(texture) = texture { - let gl_texture: &Box = std::mem::transmute(texture); + let gl_texture = texture + .as_any() + .downcast_ref::() + .ok_or(RenderError("This texture was not created by OpenGL backend.".to_string()))?; self.curr_matrix = [ [2.0 / (gl_texture.width as f32), 0.0, 0.0, 0.0], @@ -1015,10 +1023,36 @@ impl BackendRenderer for OpenGLRenderer { Ok(()) } + fn set_clip_rect(&mut self, rect: Option) -> GameResult { + if let Some((_, gl)) = self.get_context() { + unsafe { + if let Some(rect) = &rect { + gl.gl.Enable(gl::SCISSOR_TEST); + gl.gl.Scissor(rect.left as GLint, rect.top as GLint, rect.width() as GLint, rect.height() as GLint); + } else { + gl.gl.Disable(gl::SCISSOR_TEST); + } + } + + Ok(()) + } else { + Err(RenderError("No OpenGL context available!".to_string())) + } + } + fn imgui(&self) -> GameResult<&mut imgui::Context> { unsafe { Ok(&mut *self.imgui.get()) } } + fn imgui_texture_id(&self, texture: &Box) -> GameResult { + let gl_texture = texture + .as_any() + .downcast_ref::() + .ok_or(RenderError("This texture was not created by OpenGL backend.".to_string()))?; + + Ok(TextureId::new(gl_texture.texture_id as usize)) + } + fn render_imgui(&mut self, draw_data: &DrawData) -> GameResult { // https://github.com/michaelfairley/rust-imgui-opengl-renderer if let Some((_, gl)) = self.get_context() { @@ -1147,39 +1181,22 @@ impl BackendRenderer for OpenGLRenderer { Ok(()) } + fn supports_vertex_draw(&self) -> bool { + true + } + fn draw_triangle_list( &mut self, vertices: Vec, texture: Option<&Box>, shader: BackendShader, ) -> GameResult<()> { - unsafe { self.draw_arrays(gl::TRIANGLES, vertices, texture, shader) } - } - - fn supports_vertex_draw(&self) -> bool { - true - } - - fn set_clip_rect(&mut self, rect: Option) -> GameResult { - if let Some((_, gl)) = self.get_context() { - unsafe { - if let Some(rect) = &rect { - gl.gl.Enable(gl::SCISSOR_TEST); - gl.gl.Scissor(rect.left as GLint, rect.top as GLint, rect.width() as GLint, rect.height() as GLint); - } else { - gl.gl.Disable(gl::SCISSOR_TEST); - } - } - - Ok(()) - } else { - Err(RenderError("No OpenGL context available!".to_string())) - } + self.draw_arrays(gl::TRIANGLES, vertices, texture, shader) } } impl OpenGLRenderer { - unsafe fn draw_arrays( + fn draw_arrays( &mut self, vert_type: GLenum, vertices: Vec, @@ -1191,13 +1208,17 @@ impl OpenGLRenderer { } let texture_id = if let Some(texture) = texture { - let gl_texture: &Box = std::mem::transmute(texture); + let gl_texture = texture + .as_any() + .downcast_ref::() + .ok_or(RenderError("This texture was not created by OpenGL backend.".to_string()))?; + gl_texture.texture_id } else { 0 }; - self.draw_arrays_tex_id(vert_type, vertices, texture_id, shader) + unsafe { self.draw_arrays_tex_id(vert_type, vertices, texture_id, shader) } } unsafe fn draw_arrays_tex_id(