mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2025-03-24 19:09:22 +00:00
Cleanup OpenGL backend and graphics code
This commit is contained in:
parent
1b7a64b5d9
commit
56832514eb
|
@ -3,10 +3,12 @@ use std::rc::Rc;
|
|||
|
||||
use imgui::DrawData;
|
||||
|
||||
use super::context::Context;
|
||||
use super::error::GameResult;
|
||||
use super::graphics::BlendMode;
|
||||
use super::graphics::SwapMode;
|
||||
|
||||
use crate::common::{Color, Rect};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::graphics::{BlendMode, VSyncMode};
|
||||
use crate::game::Game;
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -46,7 +48,7 @@ pub trait BackendRenderer {
|
|||
|
||||
fn present(&mut self) -> GameResult;
|
||||
|
||||
fn set_vsync_mode(&mut self, _mode: VSyncMode) -> GameResult {
|
||||
fn set_swap_mode(&mut self, _mode: SwapMode) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -107,26 +109,26 @@ pub trait BackendGamepad {
|
|||
#[allow(unreachable_code)]
|
||||
pub fn init_backend(headless: bool, size_hint: (u16, u16)) -> GameResult<Box<dyn Backend>> {
|
||||
if headless {
|
||||
return crate::framework::backend_null::NullBackend::new();
|
||||
return super::backend_null::NullBackend::new();
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "backend-horizon"))]
|
||||
{
|
||||
return crate::framework::backend_horizon::HorizonBackend::new();
|
||||
return super::backend_horizon::HorizonBackend::new();
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "backend-winit"))]
|
||||
{
|
||||
return crate::framework::backend_winit::WinitBackend::new();
|
||||
return super::backend_winit::WinitBackend::new();
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend-sdl")]
|
||||
{
|
||||
return crate::framework::backend_sdl2::SDL2Backend::new(size_hint);
|
||||
return super::backend_sdl2::SDL2Backend::new(size_hint);
|
||||
}
|
||||
|
||||
log::warn!("No backend compiled in, using null backend instead.");
|
||||
crate::framework::backend_null::NullBackend::new()
|
||||
super::backend_null::NullBackend::new()
|
||||
}
|
||||
|
||||
pub enum SpriteBatchCommand {
|
||||
|
|
|
@ -32,13 +32,13 @@ use crate::framework::backend::{
|
|||
};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::{GameError, GameResult};
|
||||
use crate::framework::filesystem;
|
||||
use crate::framework::gamepad::{Axis, Button, GamepadType};
|
||||
use crate::framework::graphics::BlendMode;
|
||||
use crate::framework::graphics::{BlendMode, SwapMode};
|
||||
use crate::framework::keyboard::ScanCode;
|
||||
#[cfg(feature = "render-opengl")]
|
||||
use crate::framework::render_opengl::{GLContext, OpenGLRenderer};
|
||||
use crate::framework::ui::init_imgui;
|
||||
use crate::framework::{filesystem, render_opengl};
|
||||
use crate::game::shared_game_state::WindowMode;
|
||||
use crate::game::Game;
|
||||
use crate::game::GAME_SUSPENDED;
|
||||
|
@ -465,37 +465,39 @@ impl BackendEventLoop for SDL2EventLoop {
|
|||
key_map[ImGuiKey_Delete as usize] = Scancode::Delete as u32;
|
||||
key_map[ImGuiKey_Enter as usize] = Scancode::Return as u32;
|
||||
|
||||
let refs = self.refs.clone();
|
||||
struct SDL2GLPlatform(Rc<RefCell<SDL2Context>>);
|
||||
|
||||
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 RefCell<SDL2Context>);
|
||||
|
||||
let result = {
|
||||
let refs = &mut *refs.as_ptr();
|
||||
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 _
|
||||
};
|
||||
|
||||
*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 RefCell<SDL2Context>);
|
||||
|
||||
{
|
||||
let refs = &mut *refs.as_ptr();
|
||||
}
|
||||
|
||||
fn swap_buffers(&self) {
|
||||
let mut refs = self.0.borrow();
|
||||
refs.window.window().gl_swap_window();
|
||||
}
|
||||
|
||||
*user_data = Rc::into_raw(refs) as *mut c_void;
|
||||
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 gl_context =
|
||||
GLContext { gles2_mode: false, is_sdl: true, get_proc_address, swap_buffers, user_data, ctx };
|
||||
let platform = Box::new(SDL2GLPlatform(self.refs.clone()));
|
||||
let gl_context: GLContext = GLContext { gles2_mode: false, is_sdl: true, platform, ctx };
|
||||
|
||||
return Ok(Box::new(OpenGLRenderer::new(gl_context, imgui)));
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use crate::framework::backend::{init_backend, BackendRenderer};
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::filesystem::Filesystem;
|
||||
use crate::framework::gamepad::GamepadContext;
|
||||
use crate::framework::graphics::VSyncMode;
|
||||
use crate::framework::keyboard::KeyboardContext;
|
||||
use crate::game::Game;
|
||||
|
||||
use super::backend::{init_backend, BackendRenderer};
|
||||
use super::error::GameResult;
|
||||
use super::filesystem::Filesystem;
|
||||
use super::gamepad::GamepadContext;
|
||||
use super::graphics::SwapMode;
|
||||
use super::keyboard::KeyboardContext;
|
||||
|
||||
pub struct Context {
|
||||
pub headless: bool,
|
||||
pub shutdown_requested: bool,
|
||||
|
@ -18,7 +19,7 @@ pub struct Context {
|
|||
pub(crate) real_screen_size: (u32, u32),
|
||||
pub(crate) screen_size: (f32, f32),
|
||||
pub(crate) screen_insets: (f32, f32, f32, f32),
|
||||
pub(crate) vsync_mode: VSyncMode,
|
||||
pub(crate) swap_mode: SwapMode,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
|
@ -35,7 +36,7 @@ impl Context {
|
|||
real_screen_size: (320, 240),
|
||||
screen_size: (320.0, 240.0),
|
||||
screen_insets: (0.0, 0.0, 0.0, 0.0),
|
||||
vsync_mode: VSyncMode::Uncapped,
|
||||
swap_mode: SwapMode::VSync,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,18 +23,11 @@ pub enum BlendMode {
|
|||
Multiply,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum VSyncMode {
|
||||
/// No V-Sync - uncapped frame rate
|
||||
Uncapped,
|
||||
/// Synchronized to V-Sync
|
||||
VSync,
|
||||
/// Variable Refresh Rate - Synchronized to game tick interval
|
||||
VRRTickSync1x,
|
||||
/// Variable Refresh Rate - Synchronized to 2 * game tick interval
|
||||
VRRTickSync2x,
|
||||
/// Variable Refresh Rate - Synchronized to 3 * game tick interval
|
||||
VRRTickSync3x,
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum SwapMode {
|
||||
Immediate = 0,
|
||||
VSync = 1,
|
||||
Adaptive = -1,
|
||||
}
|
||||
|
||||
pub fn clear(ctx: &mut Context, color: Color) {
|
||||
|
@ -51,10 +44,12 @@ pub fn present(ctx: &mut Context) -> GameResult {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_vsync_mode(ctx: &mut Context, mode: VSyncMode) -> GameResult {
|
||||
pub fn set_swap_mode(ctx: &mut Context, mode: SwapMode) -> GameResult {
|
||||
if let Some(renderer) = &mut ctx.renderer {
|
||||
ctx.vsync_mode = mode;
|
||||
renderer.set_vsync_mode(mode);
|
||||
if ctx.swap_mode != mode {
|
||||
ctx.swap_mode = mode;
|
||||
renderer.set_swap_mode(mode);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -135,7 +130,6 @@ pub fn set_clip_rect(ctx: &mut Context, rect: Option<Rect>) -> GameResult {
|
|||
Err(GameError::RenderError("Rendering backend hasn't been initialized yet.".to_string()))
|
||||
}
|
||||
|
||||
|
||||
pub fn imgui_context(ctx: &Context) -> GameResult<&mut imgui::Context> {
|
||||
if let Some(renderer) = ctx.renderer.as_ref() {
|
||||
return renderer.imgui();
|
||||
|
|
|
@ -1,38 +1,41 @@
|
|||
use std::any::Any;
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
use std::borrow::BorrowMut;
|
||||
use std::cell::{Cell, RefCell, UnsafeCell};
|
||||
use std::ffi::{c_void, CStr};
|
||||
use std::hint::unreachable_unchecked;
|
||||
use std::mem;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr::null;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use imgui::{DrawCmd, DrawCmdParams, DrawData, DrawIdx, DrawVert, TextureId, Ui};
|
||||
|
||||
use super::backend::{BackendRenderer, BackendShader, BackendTexture, SpriteBatchCommand, VertexData};
|
||||
use super::context::Context;
|
||||
use super::error::GameError;
|
||||
use super::error::GameError::RenderError;
|
||||
use super::error::GameResult;
|
||||
use super::gl;
|
||||
use super::gl::types::*;
|
||||
use super::graphics::BlendMode;
|
||||
use super::graphics::SwapMode;
|
||||
use super::util::{field_offset, return_param};
|
||||
use crate::common::{Color, Rect};
|
||||
use crate::framework::backend::{BackendRenderer, BackendShader, BackendTexture, SpriteBatchCommand, VertexData};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameError;
|
||||
use crate::framework::error::GameError::RenderError;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::gl;
|
||||
use crate::framework::gl::types::*;
|
||||
use crate::framework::graphics::{BlendMode, VSyncMode};
|
||||
use crate::framework::util::{field_offset, return_param};
|
||||
use crate::game::GAME_SUSPENDED;
|
||||
|
||||
pub trait GLPlatformFunctions {
|
||||
fn get_proc_address(&self, name: &str) -> *const c_void;
|
||||
|
||||
fn swap_buffers(&self);
|
||||
|
||||
fn set_swap_mode(&self, mode: SwapMode);
|
||||
}
|
||||
|
||||
pub struct GLContext {
|
||||
pub gles2_mode: bool,
|
||||
pub is_sdl: bool,
|
||||
pub get_proc_address: unsafe fn(user_data: &mut *mut c_void, name: &str) -> *const c_void,
|
||||
pub swap_buffers: unsafe fn(user_data: &mut *mut c_void),
|
||||
pub user_data: *mut c_void,
|
||||
pub platform: Box<dyn GLPlatformFunctions>,
|
||||
pub ctx: *mut Context,
|
||||
}
|
||||
|
||||
|
@ -44,7 +47,7 @@ pub struct OpenGLTexture {
|
|||
shader: RenderShader,
|
||||
vbo: GLuint,
|
||||
vertices: Vec<VertexData>,
|
||||
context_active: Arc<RefCell<bool>>,
|
||||
gl: Rc<Gl>,
|
||||
}
|
||||
|
||||
impl BackendTexture for OpenGLTexture {
|
||||
|
@ -224,38 +227,35 @@ impl BackendTexture for OpenGLTexture {
|
|||
|
||||
fn draw(&mut self) -> GameResult {
|
||||
unsafe {
|
||||
if let Some(gl) = &GL_PROC {
|
||||
if self.texture_id == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if gl.gl.BindSampler.is_loaded() {
|
||||
gl.gl.BindSampler(0, 0);
|
||||
}
|
||||
|
||||
gl.gl.Enable(gl::TEXTURE_2D);
|
||||
gl.gl.Enable(gl::BLEND);
|
||||
gl.gl.Disable(gl::DEPTH_TEST);
|
||||
|
||||
self.shader.bind_attrib_pointer(gl, self.vbo);
|
||||
|
||||
gl.gl.BindTexture(gl::TEXTURE_2D, self.texture_id);
|
||||
gl.gl.BufferData(
|
||||
gl::ARRAY_BUFFER,
|
||||
(self.vertices.len() * mem::size_of::<VertexData>()) as _,
|
||||
self.vertices.as_ptr() as _,
|
||||
gl::STREAM_DRAW,
|
||||
);
|
||||
|
||||
gl.gl.DrawArrays(gl::TRIANGLES, 0, self.vertices.len() as _);
|
||||
|
||||
gl.gl.BindTexture(gl::TEXTURE_2D, 0);
|
||||
gl.gl.BindBuffer(gl::ARRAY_BUFFER, 0);
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RenderError("No OpenGL context available!".to_string()))
|
||||
let gl = self.gl.as_ref();
|
||||
if self.texture_id == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if gl.gl.BindSampler.is_loaded() {
|
||||
gl.gl.BindSampler(0, 0);
|
||||
}
|
||||
|
||||
gl.gl.Enable(gl::TEXTURE_2D);
|
||||
gl.gl.Enable(gl::BLEND);
|
||||
gl.gl.Disable(gl::DEPTH_TEST);
|
||||
|
||||
self.shader.bind_attrib_pointer(gl, self.vbo);
|
||||
|
||||
gl.gl.BindTexture(gl::TEXTURE_2D, self.texture_id);
|
||||
gl.gl.BufferData(
|
||||
gl::ARRAY_BUFFER,
|
||||
(self.vertices.len() * mem::size_of::<VertexData>()) as _,
|
||||
self.vertices.as_ptr() as _,
|
||||
gl::STREAM_DRAW,
|
||||
);
|
||||
|
||||
gl.gl.DrawArrays(gl::TRIANGLES, 0, self.vertices.len() as _);
|
||||
|
||||
gl.gl.BindTexture(gl::TEXTURE_2D, 0);
|
||||
gl.gl.BindBuffer(gl::ARRAY_BUFFER, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,16 +266,20 @@ impl BackendTexture for OpenGLTexture {
|
|||
|
||||
impl Drop for OpenGLTexture {
|
||||
fn drop(&mut self) {
|
||||
if *self.context_active.as_ref().borrow() {
|
||||
unsafe {
|
||||
if let Some(gl) = &GL_PROC {
|
||||
if self.texture_id != 0 {
|
||||
let texture_id = &self.texture_id;
|
||||
gl.gl.DeleteTextures(1, texture_id as *const _);
|
||||
}
|
||||
unsafe {
|
||||
let gl = self.gl.as_ref();
|
||||
if !*gl.context_active.borrow() {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.framebuffer_id != 0 {}
|
||||
}
|
||||
if self.texture_id != 0 {
|
||||
let texture_id = &self.texture_id;
|
||||
gl.gl.DeleteTextures(1, texture_id as *const _);
|
||||
}
|
||||
|
||||
if self.framebuffer_id != 0 {
|
||||
let framebuffer_id = &self.framebuffer_id;
|
||||
gl.gl.DeleteFramebuffers(1, framebuffer_id as *const _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -492,21 +496,18 @@ impl RenderData {
|
|||
// iOS has "unusual" framebuffer setup, where we can't rely on 0 as the system provided render target.
|
||||
self.render_fbo = return_param(|x| gl.gl.GetIntegerv(gl::FRAMEBUFFER_BINDING, x));
|
||||
|
||||
self.tex_shader =
|
||||
RenderShader::compile(gl, vshdr_basic, fshdr_tex).unwrap_or_else(|e| {
|
||||
log::error!("Failed to compile texture shader: {}", e);
|
||||
RenderShader::default()
|
||||
});
|
||||
self.fill_shader =
|
||||
RenderShader::compile(gl, vshdr_basic, fshdr_fill).unwrap_or_else(|e| {
|
||||
log::error!("Failed to compile fill shader: {}", e);
|
||||
RenderShader::default()
|
||||
});
|
||||
self.fill_water_shader =
|
||||
RenderShader::compile(gl, vshdr_basic, fshdr_fill_water).unwrap_or_else(|e| {
|
||||
log::error!("Failed to compile fill water shader: {}", e);
|
||||
RenderShader::default()
|
||||
});
|
||||
self.tex_shader = RenderShader::compile(gl, vshdr_basic, fshdr_tex).unwrap_or_else(|e| {
|
||||
log::error!("Failed to compile texture shader: {}", e);
|
||||
RenderShader::default()
|
||||
});
|
||||
self.fill_shader = RenderShader::compile(gl, vshdr_basic, fshdr_fill).unwrap_or_else(|e| {
|
||||
log::error!("Failed to compile fill shader: {}", e);
|
||||
RenderShader::default()
|
||||
});
|
||||
self.fill_water_shader = RenderShader::compile(gl, vshdr_basic, fshdr_fill_water).unwrap_or_else(|e| {
|
||||
log::error!("Failed to compile fill water shader: {}", e);
|
||||
RenderShader::default()
|
||||
});
|
||||
|
||||
self.vbo = return_param(|x| gl.gl.GenBuffers(1, x));
|
||||
self.ebo = return_param(|x| gl.gl.GenBuffers(1, x));
|
||||
|
@ -575,17 +576,12 @@ impl RenderData {
|
|||
|
||||
pub struct Gl {
|
||||
pub gl: gl::Gles2,
|
||||
pub context_active: RefCell<bool>,
|
||||
}
|
||||
|
||||
static mut GL_PROC: Option<Gl> = None;
|
||||
|
||||
pub fn load_gl(gl_context: &mut GLContext) -> &'static Gl {
|
||||
pub fn load_gl(gl_context: &mut GLContext) -> Gl {
|
||||
unsafe {
|
||||
if let Some(gl) = &GL_PROC {
|
||||
return gl;
|
||||
}
|
||||
|
||||
let gl = gl::Gles2::load_with(|ptr| (gl_context.get_proc_address)(&mut gl_context.user_data, ptr));
|
||||
let gl = gl::Gles2::load_with(|ptr| gl_context.platform.get_proc_address(ptr));
|
||||
|
||||
let version = {
|
||||
let p = gl.GetString(gl::VERSION);
|
||||
|
@ -599,16 +595,15 @@ pub fn load_gl(gl_context: &mut GLContext) -> &'static Gl {
|
|||
|
||||
log::info!("OpenGL version {}", version);
|
||||
|
||||
GL_PROC = Some(Gl { gl });
|
||||
GL_PROC.as_ref().unwrap()
|
||||
Gl { gl, context_active: RefCell::new(true) }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OpenGLRenderer {
|
||||
refs: GLContext,
|
||||
gl: Option<Rc<Gl>>,
|
||||
imgui: UnsafeCell<imgui::Context>,
|
||||
render_data: RenderData,
|
||||
context_active: Arc<RefCell<bool>>,
|
||||
def_matrix: [[f32; 4]; 4],
|
||||
curr_matrix: [[f32; 4]; 4],
|
||||
}
|
||||
|
@ -617,22 +612,25 @@ impl OpenGLRenderer {
|
|||
pub fn new(refs: GLContext, imgui: imgui::Context) -> OpenGLRenderer {
|
||||
OpenGLRenderer {
|
||||
refs,
|
||||
gl: None,
|
||||
imgui: UnsafeCell::new(imgui),
|
||||
render_data: RenderData::new(),
|
||||
context_active: Arc::new(RefCell::new(true)),
|
||||
def_matrix: [[0.0; 4]; 4],
|
||||
curr_matrix: [[0.0; 4]; 4],
|
||||
}
|
||||
}
|
||||
|
||||
fn get_context(&mut self) -> Option<(&mut GLContext, &'static Gl)> {
|
||||
fn get_context(&mut self) -> Option<(&mut GLContext, Rc<Gl>)> {
|
||||
let imgui = unsafe { &mut *self.imgui.get() };
|
||||
|
||||
let gles2 = self.refs.gles2_mode;
|
||||
let gl = load_gl(&mut self.refs);
|
||||
if let None = self.gl {
|
||||
self.gl = Some(Rc::new(load_gl(&mut self.refs)));
|
||||
}
|
||||
let gl = self.gl.clone().unwrap();
|
||||
|
||||
if !self.render_data.initialized {
|
||||
self.render_data.init(gles2, imgui, gl);
|
||||
self.render_data.init(gles2, imgui, &gl);
|
||||
}
|
||||
|
||||
Some((&mut self.refs, gl))
|
||||
|
@ -674,7 +672,7 @@ impl BackendRenderer for OpenGLRenderer {
|
|||
let matrix =
|
||||
[[2.0f32, 0.0, 0.0, 0.0], [0.0, -2.0, 0.0, 0.0], [0.0, 0.0, -1.0, 0.0], [-1.0, 1.0, 0.0, 1.0]];
|
||||
|
||||
self.render_data.tex_shader.bind_attrib_pointer(gl, self.render_data.vbo);
|
||||
self.render_data.tex_shader.bind_attrib_pointer(&gl, self.render_data.vbo);
|
||||
gl.gl.UniformMatrix4fv(self.render_data.tex_shader.proj_mtx, 1, gl::FALSE, matrix.as_ptr() as _);
|
||||
|
||||
let color = (255, 255, 255, 255);
|
||||
|
@ -693,46 +691,24 @@ impl BackendRenderer for OpenGLRenderer {
|
|||
self.render_data.surf_texture,
|
||||
BackendShader::Texture,
|
||||
)?;
|
||||
|
||||
//gl.gl.Finish();
|
||||
}
|
||||
|
||||
if let Some((context, _)) = self.get_context() {
|
||||
(context.swap_buffers)(&mut context.user_data);
|
||||
context.platform.swap_buffers();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_vsync_mode(&mut self, mode: VSyncMode) -> GameResult {
|
||||
if !self.refs.is_sdl {
|
||||
return Ok(());
|
||||
fn set_swap_mode(&mut self, mode: SwapMode) -> GameResult {
|
||||
if let Some((ctx, _)) = self.get_context() {
|
||||
ctx.platform.set_swap_mode(mode);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RenderError("No OpenGL context available!".to_string()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend-sdl")]
|
||||
unsafe {
|
||||
let ctx = &mut *self.refs.ctx;
|
||||
|
||||
match mode {
|
||||
VSyncMode::Uncapped => {
|
||||
sdl2_sys::SDL_GL_SetSwapInterval(0);
|
||||
}
|
||||
VSyncMode::VSync => {
|
||||
sdl2_sys::SDL_GL_SetSwapInterval(1);
|
||||
}
|
||||
_ => {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_draw(&mut self, width: f32, height: f32) -> GameResult {
|
||||
if let Some((_, gl)) = self.get_context() {
|
||||
unsafe {
|
||||
|
@ -854,7 +830,7 @@ impl BackendRenderer for OpenGLRenderer {
|
|||
vertices: Vec::new(),
|
||||
shader: self.render_data.tex_shader,
|
||||
vbo: self.render_data.vbo,
|
||||
context_active: self.context_active.clone(),
|
||||
gl: gl.clone(),
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
|
@ -893,7 +869,7 @@ impl BackendRenderer for OpenGLRenderer {
|
|||
vertices: Vec::new(),
|
||||
shader: self.render_data.tex_shader,
|
||||
vbo: self.render_data.vbo,
|
||||
context_active: self.context_active.clone(),
|
||||
gl: gl.clone(),
|
||||
}))
|
||||
}
|
||||
} else {
|
||||
|
@ -1009,7 +985,7 @@ impl BackendRenderer for OpenGLRenderer {
|
|||
|
||||
fn draw_rect(&mut self, rect: Rect<isize>, color: Color) -> GameResult {
|
||||
unsafe {
|
||||
if let Some(gl) = &GL_PROC {
|
||||
if let Some((_, gl)) = self.get_context() {
|
||||
let color = color.to_rgba();
|
||||
let mut uv = self.render_data.font_tex_size;
|
||||
uv.0 = 0.0 / uv.0;
|
||||
|
@ -1024,7 +1000,7 @@ impl BackendRenderer for OpenGLRenderer {
|
|||
VertexData { position: (rect.right as _, rect.bottom as _), uv, color },
|
||||
];
|
||||
|
||||
self.render_data.fill_shader.bind_attrib_pointer(gl, self.render_data.vbo);
|
||||
self.render_data.fill_shader.bind_attrib_pointer(&gl, self.render_data.vbo);
|
||||
|
||||
gl.gl.BindTexture(gl::TEXTURE_2D, self.render_data.font_texture);
|
||||
gl.gl.BindBuffer(gl::ARRAY_BUFFER, self.render_data.vbo);
|
||||
|
@ -1262,16 +1238,16 @@ impl OpenGLRenderer {
|
|||
mut texture: u32,
|
||||
shader: BackendShader,
|
||||
) -> GameResult<()> {
|
||||
if let Some(gl) = &GL_PROC {
|
||||
if let Some((_, gl)) = self.get_context() {
|
||||
match shader {
|
||||
BackendShader::Fill => {
|
||||
self.render_data.fill_shader.bind_attrib_pointer(gl, self.render_data.vbo)?;
|
||||
self.render_data.fill_shader.bind_attrib_pointer(&gl, self.render_data.vbo)?;
|
||||
}
|
||||
BackendShader::Texture => {
|
||||
self.render_data.tex_shader.bind_attrib_pointer(gl, self.render_data.vbo)?;
|
||||
self.render_data.tex_shader.bind_attrib_pointer(&gl, self.render_data.vbo)?;
|
||||
}
|
||||
BackendShader::WaterFill(scale, t, frame_pos) => {
|
||||
self.render_data.fill_water_shader.bind_attrib_pointer(gl, self.render_data.vbo)?;
|
||||
self.render_data.fill_water_shader.bind_attrib_pointer(&gl, self.render_data.vbo)?;
|
||||
gl.gl.Uniform1f(self.render_data.fill_water_shader.scale, scale);
|
||||
gl.gl.Uniform1f(self.render_data.fill_water_shader.time, t);
|
||||
gl.gl.Uniform2f(self.render_data.fill_water_shader.frame_offset, frame_pos.0, frame_pos.1);
|
||||
|
@ -1301,6 +1277,8 @@ impl OpenGLRenderer {
|
|||
|
||||
impl Drop for OpenGLRenderer {
|
||||
fn drop(&mut self) {
|
||||
*self.context_active.as_ref().borrow_mut() = false;
|
||||
if let Some(gl) = &self.gl {
|
||||
gl.context_active.replace(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ use scripting::tsc::text_script::ScriptMode;
|
|||
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::graphics;
|
||||
use crate::framework::graphics::VSyncMode;
|
||||
use crate::framework::graphics::{self, SwapMode};
|
||||
use crate::framework::ui::UI;
|
||||
use crate::game::filesystem_container::FilesystemContainer;
|
||||
use crate::game::settings::VSyncMode;
|
||||
use crate::game::shared_game_state::{Fps, SharedGameState, TimingMode};
|
||||
use crate::graphics::texture_set::{G_MAG, I_MAG};
|
||||
use crate::scene::loading_scene::LoadingScene;
|
||||
|
@ -61,7 +61,7 @@ pub struct Game {
|
|||
impl Game {
|
||||
fn new(ctx: &mut Context) -> GameResult<Game> {
|
||||
let s = Game {
|
||||
scene: RefCell::new( None),
|
||||
scene: RefCell::new(None),
|
||||
ui: UI::new(ctx)?,
|
||||
state: RefCell::new(SharedGameState::new(ctx)?),
|
||||
start_time: Instant::now(),
|
||||
|
@ -80,14 +80,12 @@ impl Game {
|
|||
let state_ref = self.state.get_mut();
|
||||
|
||||
if let Some(scene) = self.scene.get_mut() {
|
||||
|
||||
let speed = if state_ref.textscript_vm.mode == ScriptMode::Map
|
||||
&& state_ref.textscript_vm.flags.cutscene_skip()
|
||||
{
|
||||
4.0
|
||||
} else {
|
||||
1.0
|
||||
} * state_ref.settings.speed;
|
||||
let speed =
|
||||
if state_ref.textscript_vm.mode == ScriptMode::Map && state_ref.textscript_vm.flags.cutscene_skip() {
|
||||
4.0
|
||||
} else {
|
||||
1.0
|
||||
} * state_ref.settings.speed;
|
||||
|
||||
match state_ref.settings.timing_mode {
|
||||
TimingMode::_50Hz | TimingMode::_60Hz => {
|
||||
|
@ -133,7 +131,7 @@ impl Game {
|
|||
if let Some(mut next_scene) = next_scene {
|
||||
next_scene.init(state_ref, ctx)?;
|
||||
*self.scene.get_mut() = Some(next_scene);
|
||||
|
||||
|
||||
self.loops = 0;
|
||||
state_ref.frame_time = 0.0;
|
||||
}
|
||||
|
@ -142,14 +140,21 @@ impl Game {
|
|||
}
|
||||
|
||||
pub(crate) fn draw(&mut self, ctx: &mut Context) -> GameResult {
|
||||
match ctx.vsync_mode {
|
||||
VSyncMode::Uncapped | VSyncMode::VSync => {
|
||||
let vsync_mode = self.state.get_mut().settings.vsync_mode;
|
||||
match vsync_mode {
|
||||
VSyncMode::Uncapped => {
|
||||
graphics::set_swap_mode(ctx, SwapMode::Immediate)?;
|
||||
self.present = true;
|
||||
}
|
||||
VSyncMode::VSync => {
|
||||
graphics::set_swap_mode(ctx, SwapMode::VSync)?;
|
||||
self.present = true;
|
||||
}
|
||||
_ => unsafe {
|
||||
graphics::set_swap_mode(ctx, SwapMode::Adaptive)?;
|
||||
self.present = false;
|
||||
|
||||
let divisor = match ctx.vsync_mode {
|
||||
let divisor = match vsync_mode {
|
||||
VSyncMode::VRRTickSync1x => 1,
|
||||
VSyncMode::VRRTickSync2x => 2,
|
||||
VSyncMode::VRRTickSync3x => 3,
|
||||
|
@ -194,7 +199,8 @@ impl Game {
|
|||
|
||||
let n1 = (elapsed - self.last_tick) as f64;
|
||||
let n2 = (self.next_tick - self.last_tick) as f64;
|
||||
self.state.get_mut().frame_time = if self.state.get_mut().settings.motion_interpolation { n1 / n2 } else { 1.0 };
|
||||
self.state.get_mut().frame_time =
|
||||
if self.state.get_mut().settings.motion_interpolation { n1 / n2 } else { 1.0 };
|
||||
}
|
||||
unsafe {
|
||||
G_MAG = if self.state.get_mut().settings.subpixel_coords { self.state.get_mut().scale } else { 1.0 };
|
||||
|
|
|
@ -4,7 +4,6 @@ use crate::framework::context::Context;
|
|||
use crate::framework::error::GameResult;
|
||||
use crate::framework::filesystem::{user_create, user_open};
|
||||
use crate::framework::gamepad::{Axis, AxisDirection, Button, PlayerControllerInputType};
|
||||
use crate::framework::graphics::VSyncMode;
|
||||
use crate::framework::keyboard::ScanCode;
|
||||
use crate::game::player::TargetPlayer;
|
||||
use crate::game::shared_game_state::{CutsceneSkipMode, ScreenShakeIntensity, TimingMode, WindowMode};
|
||||
|
@ -582,6 +581,20 @@ pub fn default_controller_axis_sensitivity() -> f64 {
|
|||
0.3
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum VSyncMode {
|
||||
/// No V-Sync - uncapped frame rate
|
||||
Uncapped,
|
||||
/// Synchronized to V-Sync
|
||||
VSync,
|
||||
/// Variable Refresh Rate - Synchronized to game tick interval
|
||||
VRRTickSync1x,
|
||||
/// Variable Refresh Rate - Synchronized to 2 * game tick interval
|
||||
VRRTickSync2x,
|
||||
/// Variable Refresh Rate - Synchronized to 3 * game tick interval
|
||||
VRRTickSync3x,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum LightingEngine {
|
||||
Default,
|
||||
|
|
|
@ -2,9 +2,9 @@ use itertools::Itertools;
|
|||
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::graphics::VSyncMode;
|
||||
use crate::framework::{filesystem, graphics};
|
||||
use crate::framework::filesystem;
|
||||
use crate::game::settings::LightingEngine;
|
||||
use crate::game::settings::VSyncMode;
|
||||
use crate::game::shared_game_state::{CutsceneSkipMode, ScreenShakeIntensity, SharedGameState, TimingMode, WindowMode};
|
||||
use crate::graphics::font::Font;
|
||||
use crate::input::combined_menu_controller::CombinedMenuController;
|
||||
|
@ -725,7 +725,6 @@ impl SettingsMenu {
|
|||
|
||||
*value = new_value;
|
||||
state.settings.vsync_mode = new_mode;
|
||||
graphics::set_vsync_mode(ctx, new_mode)?;
|
||||
|
||||
let _ = state.settings.save(ctx);
|
||||
}
|
||||
|
@ -742,7 +741,6 @@ impl SettingsMenu {
|
|||
|
||||
*value = new_value;
|
||||
state.settings.vsync_mode = new_mode;
|
||||
graphics::set_vsync_mode(ctx, new_mode)?;
|
||||
|
||||
let _ = state.settings.save(ctx);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::graphics;
|
||||
use crate::game::shared_game_state::SharedGameState;
|
||||
use crate::scene::no_data_scene::NoDataScene;
|
||||
use crate::scene::Scene;
|
||||
|
@ -44,8 +43,6 @@ impl Scene for LoadingScene {
|
|||
}
|
||||
|
||||
fn draw(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
graphics::set_vsync_mode(ctx, state.settings.vsync_mode)?;
|
||||
|
||||
match state.texture_set.get_or_load_batch(ctx, &state.constants, "Loading") {
|
||||
Ok(batch) => {
|
||||
batch.add(
|
||||
|
|
Loading…
Reference in a new issue