Move rendering backend impls to framework/render

This commit is contained in:
Alula
2026-04-17 07:56:03 +02:00
parent aeb83417e8
commit a9644125ad
8 changed files with 760 additions and 610 deletions
+3 -4
View File
@@ -21,13 +21,12 @@ use super::backend::{
};
use super::context::Context;
use super::error::GameResult;
use super::filesystem;
use super::keyboard::ScanCode;
use super::render_opengl::OpenGLRenderer;
use super::{filesystem, render_opengl};
use crate::common::Rect;
use crate::framework::backend::get_scaled_size;
use crate::framework::error::GameError;
use crate::framework::render_opengl::GLContextType;
use crate::framework::render::opengl_impl::{GLContextType, GLPlatformFunctions, OpenGLRenderer};
use crate::game::shared_game_state::WindowMode;
use crate::game::Game;
use crate::input::touch_controls::TouchPoint;
@@ -386,7 +385,7 @@ impl BackendEventLoop for GlutinEventLoop {
fn new_renderer(&self, ctx: &mut Context) -> GameResult<Box<dyn BackendRenderer>> {
struct GlutinGLPlatform(Refs);
impl render_opengl::GLPlatformFunctions for GlutinGLPlatform {
impl GLPlatformFunctions for GlutinGLPlatform {
fn get_proc_address(&self, name: &str) -> *const c_void {
let window = self.0.borrow();
if let Some(window) = window.as_ref() {
+1 -85
View File
@@ -13,6 +13,7 @@ use crate::framework::backend::{
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::framework::graphics::BlendMode;
use crate::framework::render::null_impl::NullRenderer;
use crate::game::Game;
pub struct NullBackend;
@@ -63,88 +64,3 @@ impl BackendEventLoop for NullEventLoop {
self
}
}
pub struct NullTexture(u16, u16);
impl BackendTexture for NullTexture {
fn dimensions(&self) -> (u16, u16) {
(self.0, self.1)
}
fn add(&mut self, _command: SpriteBatchCommand) {}
fn clear(&mut self) {}
fn draw(&mut self) -> GameResult<()> {
Ok(())
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct NullRenderer;
impl BackendRenderer for NullRenderer {
fn renderer_name(&self) -> String {
"Null".to_owned()
}
fn clear(&mut self, _color: Color) {}
fn present(&mut self) -> GameResult {
Ok(())
}
fn create_texture_mutable(&mut self, width: u16, height: u16) -> GameResult<Box<dyn BackendTexture>> {
Ok(Box::new(NullTexture(width, height)))
}
fn create_texture(&mut self, width: u16, height: u16, _data: &[u8]) -> GameResult<Box<dyn BackendTexture>> {
Ok(Box::new(NullTexture(width, height)))
}
fn set_blend_mode(&mut self, _blend: BlendMode) -> GameResult {
Ok(())
}
fn set_render_target(&mut self, _texture: Option<&Box<dyn BackendTexture>>) -> GameResult {
Ok(())
}
fn draw_rect(&mut self, _rect: Rect<isize>, _color: Color) -> GameResult {
Ok(())
}
fn draw_outline_rect(&mut self, _rect: Rect<isize>, _line_width: usize, _color: Color) -> GameResult {
Ok(())
}
fn set_clip_rect(&mut self, _rect: Option<Rect>) -> GameResult {
Ok(())
}
fn draw_triangles(
&mut self,
_vertices: &[VertexData],
_texture: Option<&Box<dyn BackendTexture>>,
_shader: BackendShader,
) -> GameResult<()> {
Ok(())
}
fn draw_triangles_indexed(
&mut self,
vertices: &[VertexData],
indices: super::graphics::IndexData,
texture: Option<&Box<dyn BackendTexture>>,
shader: BackendShader,
) -> GameResult {
Ok(())
}
fn as_any(&self) -> &dyn Any {
self
}
}
File diff suppressed because it is too large Load Diff
+1 -2
View File
@@ -14,8 +14,7 @@ pub mod filesystem;
pub mod gamepad;
pub mod graphics;
pub mod keyboard;
#[cfg(feature = "render-opengl")]
pub mod render_opengl;
pub mod render;
pub mod ui;
pub mod util;
pub mod vfs;
+90
View File
@@ -0,0 +1,90 @@
#![allow(unused)]
use crate::common::{Color, Rect};
use crate::framework::backend::BackendTexture;
use crate::framework::error::GameResult;
use crate::framework::graphics::{BlendMode, FilterMode};
pub trait SpriteRenderer {
/// Set blending mode used for subsequent draws.
fn set_blend_mode(&mut self, mode: BlendMode);
/// Set texture filtering used for subsequent textured draws.
fn set_filter_mode(&mut self, mode: FilterMode);
/// Set scissor/clip rectangle in destination pixels; None disables clipping.
fn set_clip_rect(&mut self, rect: Option<Rect>);
/// Save the current state (blend/filter/clip) and return an RAII guard that
/// restores it when dropped.
fn save<'a>(&'a mut self) -> StateGuard<'a>;
/// Restore the most recently saved state. Intended for RAII guard use.
fn restore_state(&mut self, guard: &StateGuard);
/// Draw a filled rectangle in destination pixels with the current state.
fn fill_rect(&mut self, rect: Rect, color: Color) -> GameResult;
/// Draw a rectangle outline with given line width in pixels.
fn draw_rect(&mut self, rect: Rect, line_width: usize, color: Color) -> GameResult;
fn copy(&mut self, texture: &Box<dyn BackendTexture>, src: Option<Rect<u16>>, dst: Rect<f32>) -> GameResult;
fn copy_tinted(
&mut self,
texture: &Box<dyn BackendTexture>,
src: Option<Rect<u16>>,
dst: Rect<f32>,
color: Color,
) -> GameResult;
fn copy_ex(
&mut self,
texture: &Box<dyn BackendTexture>,
src: Option<Rect<u16>>,
dst: Rect<f32>,
angle_deg: f32,
center: Option<(f32, f32)>,
flip_x: bool,
flip_y: bool,
color: Color,
) -> GameResult;
/// Ensure any buffered draws are submitted to the underlying backend.
fn flush(&mut self) -> GameResult;
}
/// RAII guard that restores renderer state on drop.
pub struct StateGuard<'a> {
renderer: Option<&'a mut dyn SpriteRenderer>,
state: SavedState,
}
impl<'a> StateGuard<'a> {
pub(crate) fn with_state(renderer: &'a mut dyn SpriteRenderer, state: SavedState) -> Self {
Self { renderer: Some(renderer), state }
}
}
impl<'a> Drop for StateGuard<'a> {
fn drop(&mut self) {
if let Some(renderer) = self.renderer.take() {
renderer.restore_state(self);
}
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct SavedState {
blend: BlendMode,
filter: FilterMode,
clip: Option<Rect>,
}
pub mod null_impl;
#[cfg(feature = "render-opengl")]
pub mod opengl_impl;
#[cfg(feature = "backend-sdl")]
pub mod sdl2_impl;
+91
View File
@@ -0,0 +1,91 @@
use std::any::Any;
use crate::common::{Color, Rect};
use crate::framework::backend::{BackendRenderer, BackendShader, BackendTexture, SpriteBatchCommand, VertexData};
use crate::framework::error::GameResult;
use crate::framework::graphics::{BlendMode, IndexData};
pub struct NullTexture(u16, u16);
impl BackendTexture for NullTexture {
fn dimensions(&self) -> (u16, u16) {
(self.0, self.1)
}
fn add(&mut self, _command: SpriteBatchCommand) {}
fn clear(&mut self) {}
fn draw(&mut self) -> GameResult<()> {
Ok(())
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct NullRenderer;
impl BackendRenderer for NullRenderer {
fn renderer_name(&self) -> String {
"Null".to_owned()
}
fn clear(&mut self, _color: Color) {}
fn present(&mut self) -> GameResult {
Ok(())
}
fn create_texture_mutable(&mut self, width: u16, height: u16) -> GameResult<Box<dyn BackendTexture>> {
Ok(Box::new(NullTexture(width, height)))
}
fn create_texture(&mut self, width: u16, height: u16, _data: &[u8]) -> GameResult<Box<dyn BackendTexture>> {
Ok(Box::new(NullTexture(width, height)))
}
fn set_blend_mode(&mut self, _blend: BlendMode) -> GameResult {
Ok(())
}
fn set_render_target(&mut self, _texture: Option<&Box<dyn BackendTexture>>) -> GameResult {
Ok(())
}
fn draw_rect(&mut self, _rect: Rect<isize>, _color: Color) -> GameResult {
Ok(())
}
fn draw_outline_rect(&mut self, _rect: Rect<isize>, _line_width: usize, _color: Color) -> GameResult {
Ok(())
}
fn set_clip_rect(&mut self, _rect: Option<Rect>) -> GameResult {
Ok(())
}
fn draw_triangles(
&mut self,
_vertices: &[VertexData],
_texture: Option<&Box<dyn BackendTexture>>,
_shader: BackendShader,
) -> GameResult<()> {
Ok(())
}
fn draw_triangles_indexed(
&mut self,
vertices: &[VertexData],
indices: IndexData,
texture: Option<&Box<dyn BackendTexture>>,
shader: BackendShader,
) -> GameResult {
Ok(())
}
fn as_any(&self) -> &dyn Any {
self
}
}
@@ -12,14 +12,14 @@ use std::sync::Arc;
use glow::{HasContext, PixelUnpackData};
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::graphics::{BlendMode, IndexData, ShaderStage, 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::graphics::{BlendMode, IndexData, ShaderStage, SwapMode};
use crate::framework::util::{field_offset, return_param};
type GLResult<T = ()> = Result<T, String>;
@@ -324,14 +324,14 @@ fn check_shader_compile_status(shader: glow::Shader, gl: &glow::Context) -> GLRe
Ok(())
}
const VERTEX_SHADER_BASIC: &str = include_str!("shaders/opengl/vertex_basic_110.glsl");
const FRAGMENT_SHADER_TEXTURED: &str = include_str!("shaders/opengl/fragment_textured_110.glsl");
const FRAGMENT_SHADER_COLOR: &str = include_str!("shaders/opengl/fragment_color_110.glsl");
const FRAGMENT_SHADER_WATER: &str = include_str!("shaders/opengl/fragment_water_110.glsl");
const VERTEX_SHADER_BASIC: &str = include_str!("../shaders/opengl/vertex_basic_110.glsl");
const FRAGMENT_SHADER_TEXTURED: &str = include_str!("../shaders/opengl/fragment_textured_110.glsl");
const FRAGMENT_SHADER_COLOR: &str = include_str!("../shaders/opengl/fragment_color_110.glsl");
const FRAGMENT_SHADER_WATER: &str = include_str!("../shaders/opengl/fragment_water_110.glsl");
const VERTEX_SHADER_BASIC_GLES: &str = include_str!("shaders/opengles/vertex_basic_100.glsl");
const FRAGMENT_SHADER_TEXTURED_GLES: &str = include_str!("shaders/opengles/fragment_textured_100.glsl");
const FRAGMENT_SHADER_COLOR_GLES: &str = include_str!("shaders/opengles/fragment_color_100.glsl");
const VERTEX_SHADER_BASIC_GLES: &str = include_str!("../shaders/opengles/vertex_basic_100.glsl");
const FRAGMENT_SHADER_TEXTURED_GLES: &str = include_str!("../shaders/opengles/fragment_textured_100.glsl");
const FRAGMENT_SHADER_COLOR_GLES: &str = include_str!("../shaders/opengles/fragment_color_100.glsl");
macro_rules! impl_rtti {
($name:ident, $inner_type:ty, $create_method:ident, $delete_method:ident) => {
@@ -415,6 +415,7 @@ impl Drop for RenderShaderObject {
}
struct RenderShader {
name: String,
program_id: Option<glow::Program>,
vertex_shader: Rc<RenderShaderObject>,
fragment_shader: Rc<RenderShaderObject>,
@@ -434,9 +435,11 @@ impl RenderShader {
context: &GlContextHolder,
vertex_shader: Rc<RenderShaderObject>,
fragment_shader: Rc<RenderShaderObject>,
name: String,
) -> GLResult<Rc<RenderShader>> {
unsafe {
let mut shader = RenderShader {
name,
program_id: None,
vertex_shader,
fragment_shader,
@@ -458,6 +461,8 @@ impl RenderShader {
gl.attach_shader(program_id, shader.vertex_shader.shader);
gl.attach_shader(program_id, shader.fragment_shader.shader);
gl.link_program(program_id);
// TODO: Error check?
log::debug!("Linked shader '{0}', program ID: {program_id:?}", shader.name);
shader.texture = gl.get_uniform_location(program_id, "Texture");
shader.proj_mtx = gl.get_uniform_location(program_id, "ProjMtx");
@@ -570,9 +575,12 @@ impl RenderData {
let mut surf_texture = TextureRAAI::new(gl)?;
let mut surf_framebuffer = FramebufferRAAI::new(gl)?;
let tex_shader = RenderShader::compile(&context, vshdr_basic.clone(), fshdr_tex)?;
let fill_shader = RenderShader::compile(&context, vshdr_basic.clone(), fshdr_fill)?;
let fill_water_shader = RenderShader::compile(&context, vshdr_basic.clone(), fshdr_fill_water)?;
let tex_shader =
RenderShader::compile(&context, vshdr_basic.clone(), fshdr_tex, "builtin texture".to_owned())?;
let fill_shader =
RenderShader::compile(&context, vshdr_basic.clone(), fshdr_fill, "builtin fill".to_owned())?;
let fill_water_shader =
RenderShader::compile(&context, vshdr_basic.clone(), fshdr_fill_water, "builtin water".to_owned())?;
let vbo = vbo.take();
let ebo = ebo.take();
@@ -1144,11 +1152,34 @@ impl OpenGLRenderer {
#[cfg(debug_assertions)]
unsafe {
if gl.supports_debug() {
log::info!("Debug output is supported");
gl.enable(glow::DEBUG_OUTPUT);
gl.enable(glow::DEBUG_OUTPUT_SYNCHRONOUS);
gl.debug_message_callback(|source, type_, id, severity, message| {
log::info!("Debug message: {} {} {} {} {}", source, type_, id, severity, message);
let type_str = match type_ {
glow::DEBUG_TYPE_DEPRECATED_BEHAVIOR => "DEPRECATED_BEHAVIOR",
glow::DEBUG_TYPE_ERROR => "ERROR",
glow::DEBUG_TYPE_MARKER => "MARKER",
glow::DEBUG_TYPE_OTHER => "OTHER",
glow::DEBUG_TYPE_PERFORMANCE => "PERFORMANCE",
glow::DEBUG_TYPE_POP_GROUP => "POP_GROUP",
glow::DEBUG_TYPE_PORTABILITY => "PORTABILITY",
glow::DEBUG_TYPE_PUSH_GROUP => "PUSH_GROUP",
glow::DEBUG_TYPE_UNDEFINED_BEHAVIOR => "UNDEFINED_BEHAVIOR",
_ => "UNKNOWN",
};
if severity == glow::DEBUG_SEVERITY_NOTIFICATION {
return; // too spammy
}
let severity_str = match severity {
glow::DEBUG_SEVERITY_NOTIFICATION => "NOTIFICATION",
glow::DEBUG_SEVERITY_HIGH => "HIGH",
glow::DEBUG_SEVERITY_MEDIUM => "MEDIUM",
glow::DEBUG_SEVERITY_LOW => "LOW",
_ => "UNKNOWN",
};
log::debug!("GLDebugOutput(type={type_str}, id={id}, severity={severity_str}): {message}");
});
}
}
File diff suppressed because it is too large Load Diff