From 2c9c150e5e08cc3dc7128297e8d64daaf2c60177 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sun, 25 Apr 2021 23:38:48 +0200 Subject: [PATCH] renderer: Remove `Frame` trait and merge into `Renderer` Tracking of Frames, so that only one unique one can exist at a time (gles does not allow multiple frames being rendered in parallel) lead to very unfriendly lifetime-heavy code. A renderer is already *unique*, just move the code there and add an error variant to catch misuses. --- anvil/src/drawing.rs | 59 +++-- anvil/src/winit.rs | 14 +- src/backend/renderer/gles2/mod.rs | 408 +++++++++++++++--------------- src/backend/renderer/mod.rs | 17 +- src/backend/winit.rs | 51 ++-- 5 files changed, 274 insertions(+), 275 deletions(-) diff --git a/anvil/src/drawing.rs b/anvil/src/drawing.rs index cc5f4f9..fbafba6 100644 --- a/anvil/src/drawing.rs +++ b/anvil/src/drawing.rs @@ -14,17 +14,16 @@ use smithay::{ use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData}; -pub fn draw_cursor( + +pub fn draw_cursor( renderer: &mut R, - frame: &mut F, surface: &wl_surface::WlSurface, (x, y): (i32, i32), token: MyCompositorToken, log: &Logger, ) where - R: Renderer, - F: Frame, + R: Renderer, E: std::error::Error, T: Texture + 'static, { @@ -38,27 +37,25 @@ pub fn draw_cursor( (0, 0) } }; - draw_surface_tree(renderer, frame, surface, (x - dx, y - dy), token, log); + draw_surface_tree(renderer, surface, (x - dx, y - dy), token, log); } -fn draw_surface_tree( +fn draw_surface_tree( renderer: &mut R, - frame: &mut F, root: &wl_surface::WlSurface, location: (i32, i32), compositor_token: MyCompositorToken, log: &Logger, ) where - R: Renderer, - F: Frame, + R: Renderer, E: std::error::Error, T: Texture + 'static, { compositor_token.with_surface_tree_upward( root, - location, - |_surface, attributes, role, &(mut x, mut y)| { + (), + |_surface, attributes, role, _| { // Pull a new buffer if available if let Some(data) = attributes.user_data.get::>() { let mut data = data.borrow_mut(); @@ -76,6 +73,29 @@ fn draw_surface_tree( } } // Now, should we be drawn ? + if data.texture.is_some() { + TraversalAction::DoChildren(()) + } else { + // we are not displayed, so our children are neither + TraversalAction::SkipChildren + } + } else { + // we are not displayed, so our children are neither + TraversalAction::SkipChildren + } + }, + |_, _, _, _| {}, + |_, _, _, _| true, + ); + + compositor_token.with_surface_tree_upward( + root, + location, + |_surface, attributes, role, &(mut x, mut y)| { + // Pull a new buffer if available + if let Some(data) = attributes.user_data.get::>() { + let mut data = data.borrow_mut(); + // Now, should we be drawn ? if data.texture.is_some() { // if yes, also process the children if Role::::has(role) { @@ -103,7 +123,7 @@ fn draw_surface_tree( x += sub_x; y += sub_y; } - frame.render_texture_at(&*buffer_texture, (x, y), Transform::Normal /* TODO */, 1.0); + renderer.render_texture_at(&*buffer_texture, (x, y), Transform::Normal /* TODO */, 1.0); } } }, @@ -111,17 +131,15 @@ fn draw_surface_tree( ); } -pub fn draw_windows( +pub fn draw_windows( renderer: &mut R, - frame: &mut F, window_map: &MyWindowMap, output_rect: Option, compositor_token: MyCompositorToken, log: &::slog::Logger, ) where - R: Renderer, - F: Frame, + R: Renderer, E: std::error::Error, T: Texture + 'static, { @@ -140,7 +158,6 @@ pub fn draw_windows( // this surface is a root of a subsurface tree that needs to be drawn draw_surface_tree( renderer, - frame, &wl_surface, initial_place, compositor_token, @@ -152,17 +169,15 @@ pub fn draw_windows( } } -pub fn draw_dnd_icon( +pub fn draw_dnd_icon( renderer: &mut R, - frame: &mut F, surface: &wl_surface::WlSurface, (x, y): (i32, i32), token: MyCompositorToken, log: &::slog::Logger, ) where - R: Renderer, - F: Frame, + R: Renderer, E: std::error::Error, T: Texture + 'static, { @@ -172,7 +187,7 @@ pub fn draw_dnd_icon( "Trying to display as a dnd icon a surface that does not have the DndIcon role." ); } - draw_surface_tree(renderer, frame, surface, (x, y), token, log); + draw_surface_tree(renderer, surface, (x, y), token, log); } /* diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index aa52584..8b3d6cb 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -3,7 +3,7 @@ use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration}; //#[cfg(feature = "egl")] //use smithay::backend::egl::EGLGraphicsBackend; use smithay::{ - backend::{renderer::Frame, input::InputBackend, winit, SwapBuffersError}, + backend::{renderer::Renderer, input::InputBackend, winit, SwapBuffersError}, reexports::{ calloop::EventLoop, wayland_server::{protocol::wl_output, Display}, @@ -112,11 +112,11 @@ pub fn run_winit( // drawing logic { - let mut frame = renderer.begin().expect("Failed to render frame"); - frame.clear([0.8, 0.8, 0.9, 1.0]); + renderer.begin().expect("Failed to render frame"); + renderer.clear([0.8, 0.8, 0.9, 1.0]).expect("Failed to clear frame"); // draw the windows - draw_windows(&mut renderer, &mut frame, &*state.window_map.borrow(), None, state.ctoken, &log); + draw_windows(&mut renderer, &*state.window_map.borrow(), None, state.ctoken, &log); let (x, y) = *state.pointer_location.borrow(); // draw the dnd icon if any @@ -124,7 +124,7 @@ pub fn run_winit( let guard = state.dnd_icon.lock().unwrap(); if let Some(ref surface) = *guard { if surface.as_ref().is_alive() { - draw_dnd_icon(&mut renderer, &mut frame, surface, (x as i32, y as i32), state.ctoken, &log); + draw_dnd_icon(&mut renderer, surface, (x as i32, y as i32), state.ctoken, &log); } } } @@ -143,13 +143,13 @@ pub fn run_winit( // draw as relevant if let CursorImageStatus::Image(ref surface) = *guard { renderer.window().set_cursor_visible(false); - draw_cursor(&mut renderer, &mut frame, surface, (x as i32, y as i32), state.ctoken, &log); + draw_cursor(&mut renderer, surface, (x as i32, y as i32), state.ctoken, &log); } else { renderer.window().set_cursor_visible(true); } } - if let Err(SwapBuffersError::ContextLost(err)) = frame.finish() { + if let Err(SwapBuffersError::ContextLost(err)) = renderer.finish() { error!(log, "Critical Rendering Error: {}", err); state.running.store(false, Ordering::SeqCst); } diff --git a/src/backend/renderer/gles2/mod.rs b/src/backend/renderer/gles2/mod.rs index 71c49fb..e910677 100644 --- a/src/backend/renderer/gles2/mod.rs +++ b/src/backend/renderer/gles2/mod.rs @@ -1,15 +1,15 @@ use std::collections::HashSet; use std::ffi::CStr; use std::ptr; -use std::sync::Arc; +use std::rc::Rc; use cgmath::{prelude::*, Matrix3, Vector2}; mod shaders; use crate::backend::SwapBuffersError; use crate::backend::allocator::{dmabuf::{Dmabuf, WeakDmabuf}, Format}; -use crate::backend::egl::{EGLContext, EGLSurface, ffi::egl::types::EGLImage}; -use super::{Renderer, Frame, Bind, Unbind, Transform, Texture}; +use crate::backend::egl::{EGLContext, EGLSurface, ffi::egl::types::EGLImage, MakeCurrentError}; +use super::{Renderer, Bind, Unbind, Transform, Texture}; #[cfg(feature = "wayland_frontend")] use wayland_server::protocol::{wl_shm, wl_buffer}; @@ -30,6 +30,7 @@ struct Gles2Program { attrib_tex_coords: ffi::types::GLint, } +// TODO: drop? pub struct Gles2Texture { texture: ffi::types::GLuint, texture_kind: usize, @@ -62,22 +63,16 @@ struct Gles2Buffer { } pub struct Gles2Renderer { - internal: Arc, buffers: Vec, - current_buffer: Option, -} - -struct Gles2RendererInternal { - gl: ffi::Gles2, - egl: EGLContext, + target_buffer: Option, + target_surface: Option>, + current_projection: Option>, extensions: Vec, programs: [Gles2Program; shaders::FRAGMENT_COUNT], + gl: ffi::Gles2, + egl: EGLContext, logger: Option<*mut ::slog::Logger>, -} - -pub struct Gles2Frame { - internal: Arc, - projection: Matrix3, + _not_send: *mut (), } #[derive(thiserror::Error, Debug)] @@ -103,6 +98,8 @@ pub enum Gles2Error { #[error("Error accessing the buffer ({0:?})")] #[cfg(feature = "wayland_frontend")] BufferAccessError(crate::wayland::shm::BufferAccessError), + #[error("Call begin before doing any rendering operations")] + UnconstraintRenderingOperation, } impl From for SwapBuffersError { @@ -112,6 +109,7 @@ impl From for SwapBuffersError { | x @ Gles2Error::ProgramLinkError | x @ Gles2Error::GLFunctionLoaderError | x @ Gles2Error::GLExtensionNotSupported(_) + | x @ Gles2Error::UnconstraintRenderingOperation => SwapBuffersError::ContextLost(Box::new(x)), Gles2Error::ContextActivationError(err) => err.into(), x @ Gles2Error::FramebufferBindingError @@ -205,15 +203,15 @@ unsafe fn texture_program(gl: &ffi::Gles2, frag: &'static str) -> Result(context: EGLContext, logger: L) -> Result + pub unsafe fn new(context: EGLContext, logger: L) -> Result where L: Into> { let log = crate::slog_or_fallback(logger).new(o!("smithay_module" => "renderer_gles2")); - unsafe { context.make_current()? }; + context.make_current()?; - let (gl, exts, logger) = unsafe { + let (gl, exts, logger) = { let gl = ffi::Gles2::load_with(|s| crate::backend::egl::get_proc_address(s) as *const _); let ext_ptr = gl.GetString(ffi::EXTENSIONS) as *const i8; if ext_ptr.is_null() { @@ -252,52 +250,55 @@ impl Gles2Renderer { (gl, exts, logger) }; - let programs = { - unsafe { [ + let programs = [ texture_program(&gl, shaders::FRAGMENT_SHADER_ABGR)?, texture_program(&gl, shaders::FRAGMENT_SHADER_XBGR)?, texture_program(&gl, shaders::FRAGMENT_SHADER_BGRA)?, texture_program(&gl, shaders::FRAGMENT_SHADER_BGRX)?, texture_program(&gl, shaders::FRAGMENT_SHADER_EXTERNAL)?, - ] } - }; + ]; - Ok(Gles2Renderer { - internal: Arc::new(Gles2RendererInternal { - gl, - egl: context, - extensions: exts, - programs, - logger, - }), + let renderer = Gles2Renderer { + gl, + egl: context, + extensions: exts, + programs, + logger, + target_buffer: None, + target_surface: None, buffers: Vec::new(), - current_buffer: None, - }) + current_projection: None, + _not_send: std::ptr::null_mut(), + }; + renderer.egl.unbind()?; + Ok(renderer) + } + + fn make_current(&self) -> Result<(), MakeCurrentError> { + unsafe { + if let Some(surface) = self.target_surface.as_ref() { + self.egl.make_current_with_surface(surface)?; + } else { + self.egl.make_current()?; + } + } + Ok(()) } } -impl Bind<&EGLSurface> for Gles2Renderer { - fn bind(&mut self, surface: &EGLSurface) -> Result<(), Gles2Error> { - if self.current_buffer.is_some() { - self.unbind()?; - } - - unsafe { - self.internal.egl.make_current_with_surface(&surface)?; - } - +impl Bind> for Gles2Renderer { + fn bind(&mut self, surface: Rc) -> Result<(), Gles2Error> { + self.unbind()?; + self.target_surface = Some(surface); Ok(()) } } impl Bind for Gles2Renderer { fn bind(&mut self, dmabuf: Dmabuf) -> Result<(), Gles2Error> { - if self.current_buffer.is_some() { - self.unbind()?; - } - + self.unbind()?; unsafe { - self.internal.egl.make_current()?; + self.egl.make_current()?; } // Free outdated buffer resources @@ -323,21 +324,21 @@ impl Bind for Gles2Renderer { }) }) .unwrap_or_else(|| { - let image = self.internal.egl.display.create_image_from_dmabuf(&dmabuf).map_err(Gles2Error::BindBufferEGLError)?; + let image = self.egl.display.create_image_from_dmabuf(&dmabuf).map_err(Gles2Error::BindBufferEGLError)?; unsafe { let mut rbo = 0; - self.internal.gl.GenRenderbuffers(1, &mut rbo as *mut _); - self.internal.gl.BindRenderbuffer(ffi::RENDERBUFFER, rbo); - self.internal.gl.EGLImageTargetRenderbufferStorageOES(ffi::RENDERBUFFER, image); - self.internal.gl.BindRenderbuffer(ffi::RENDERBUFFER, 0); + self.gl.GenRenderbuffers(1, &mut rbo as *mut _); + self.gl.BindRenderbuffer(ffi::RENDERBUFFER, rbo); + self.gl.EGLImageTargetRenderbufferStorageOES(ffi::RENDERBUFFER, image); + self.gl.BindRenderbuffer(ffi::RENDERBUFFER, 0); let mut fbo = 0; - self.internal.gl.GenFramebuffers(1, &mut fbo as *mut _); - self.internal.gl.BindFramebuffer(ffi::FRAMEBUFFER, fbo); - self.internal.gl.FramebufferRenderbuffer(ffi::FRAMEBUFFER, ffi::COLOR_ATTACHMENT0, ffi::RENDERBUFFER, rbo); - let status = self.internal.gl.CheckFramebufferStatus(ffi::FRAMEBUFFER); - self.internal.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0); + self.gl.GenFramebuffers(1, &mut fbo as *mut _); + self.gl.BindFramebuffer(ffi::FRAMEBUFFER, fbo); + self.gl.FramebufferRenderbuffer(ffi::FRAMEBUFFER, ffi::COLOR_ATTACHMENT0, ffi::RENDERBUFFER, rbo); + let status = self.gl.CheckFramebufferStatus(ffi::FRAMEBUFFER); + self.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0); if status != ffi::FRAMEBUFFER_COMPLETE { //TODO wrap image and drop here @@ -361,146 +362,36 @@ impl Bind for Gles2Renderer { })?; unsafe { - self.internal.gl.BindFramebuffer(ffi::FRAMEBUFFER, buffer.internal.fbo); + self.gl.BindFramebuffer(ffi::FRAMEBUFFER, buffer.internal.fbo); } - self.current_buffer = Some(buffer); + + self.target_buffer = Some(buffer); Ok(()) } fn supported_formats(&self) -> Option> { - Some(self.internal.egl.display.dmabuf_render_formats.clone()) + Some(self.egl.display.dmabuf_render_formats.clone()) } } impl Unbind for Gles2Renderer { - fn unbind(&mut self) -> Result<(), Gles2Error> { + fn unbind(&mut self) -> Result<(), ::Error> { unsafe { - self.internal.egl.make_current()?; - self.internal.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0); + self.egl.make_current()?; } - self.current_buffer = None; - let _ = self.internal.egl.unbind(); + unsafe { self.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0) }; + self.target_buffer = None; + self.target_surface = None; + self.egl.unbind()?; Ok(()) } } -impl Renderer for Gles2Renderer { - type Error = Gles2Error; - type Texture = Gles2Texture; - type Frame = Gles2Frame; - - fn begin(&mut self, width: u32, height: u32, transform: Transform) -> Result { - if !self.internal.egl.is_current() { - // Do not call this unconditionally. - // If surfaces are in use (e.g. for winit) this would unbind them. - unsafe { self.internal.egl.make_current()?; } - } - unsafe { - self.internal.gl.Viewport(0, 0, width as i32, height as i32); - - self.internal.gl.Enable(ffi::BLEND); - self.internal.gl.BlendFunc(ffi::ONE, ffi::ONE_MINUS_SRC_ALPHA); - } - - // output transformation passed in by the user - let mut projection = Matrix3::::identity(); - projection = projection * Matrix3::from_translation(Vector2::new(width as f32 / 2.0, height as f32 / 2.0)); - projection = projection * transform.matrix(); - let (transformed_width, transformed_height) = transform.transform_size(width, height); - projection = projection * Matrix3::from_translation(Vector2::new(-(transformed_width as f32) / 2.0, -(transformed_height as f32) / 2.0)); - - // replicate https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml - // glOrtho(0, width, 0, height, 1, 1); - let mut renderer = Matrix3::::identity(); - let t = Matrix3::::identity(); - let x = 2.0 / (width as f32); - let y = 2.0 / (height as f32); - - // Rotation & Reflection - renderer[0][0] = x * t[0][0]; - renderer[1][0] = x * t[0][1]; - renderer[0][1] = y * -t[1][0]; - renderer[1][1] = y * -t[1][1]; - - //Translation - renderer[2][0] = -(1.0f32.copysign(renderer[0][0] + renderer[1][0])); - renderer[2][1] = -(1.0f32.copysign(renderer[0][1] + renderer[1][1])); - - Ok(Gles2Frame { - internal: self.internal.clone(), - projection: projection * renderer, - }) - } - - #[cfg(feature = "wayland_frontend")] - fn shm_formats(&self) -> &[wl_shm::Format] { - &[ - wl_shm::Format::Abgr8888, - wl_shm::Format::Xbgr8888, - wl_shm::Format::Argb8888, - wl_shm::Format::Xrgb8888, - ] - } - - #[cfg(feature = "wayland_frontend")] - fn import_shm(&self, buffer: &wl_buffer::WlBuffer) -> Result { - use crate::wayland::shm::with_buffer_contents; - - with_buffer_contents(&buffer, |slice, data| { - if !self.internal.egl.is_current() { - unsafe { self.internal.egl.make_current()?; } - } - - let offset = data.offset as i32; - let width = data.width as i32; - let height = data.height as i32; - let stride = data.stride as i32; - - // number of bytes per pixel - // TODO: compute from data.format - let pixelsize = 4i32; - - // ensure consistency, the SHM handler of smithay should ensure this - assert!((offset + (height - 1) * stride + width * pixelsize) as usize <= slice.len()); - - let (gl_format, shader_idx) = match data.format { - wl_shm::Format::Abgr8888 => (ffi::RGBA, 0), - wl_shm::Format::Xbgr8888 => (ffi::RGBA, 1), - wl_shm::Format::Argb8888 => (ffi::BGRA_EXT, 2), - wl_shm::Format::Xrgb8888 => (ffi::BGRA_EXT, 3), - format => return Err(Gles2Error::UnsupportedPixelFormat(format)), - }; - - let mut tex = 0; - unsafe { - self.internal.gl.GenTextures(1, &mut tex); - self.internal.gl.BindTexture(ffi::TEXTURE_2D, tex); - - self.internal.gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::CLAMP_TO_EDGE as i32); - self.internal.gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_T, ffi::CLAMP_TO_EDGE as i32); - self.internal.gl.PixelStorei(ffi::UNPACK_ROW_LENGTH, stride / pixelsize); - self.internal.gl.TexImage2D(ffi::TEXTURE_2D, 0, gl_format as i32, width, height, 0, gl_format, ffi::UNSIGNED_BYTE as u32, slice.as_ptr() as *const _); - - self.internal.gl.PixelStorei(ffi::UNPACK_ROW_LENGTH, 0); - self.internal.gl.BindTexture(ffi::TEXTURE_2D, 0); - } - - Ok(Gles2Texture { - texture: tex, - texture_kind: shader_idx, - is_external: false, - y_inverted: false, - width: width as u32, - height: height as u32, - }) - }).map_err(Gles2Error::BufferAccessError)? - } -} - -impl Drop for Gles2RendererInternal { +impl Drop for Gles2Renderer { fn drop(&mut self) { unsafe { if self.egl.make_current().is_ok() { + self.gl.BindFramebuffer(ffi::FRAMEBUFFER, 0); for program in &self.programs { self.gl.DeleteProgram(program.program); } @@ -533,60 +424,171 @@ static TEX_COORDS: [ffi::types::GLfloat; 8] = [ 0.0, 1.0, // bottom left ]; -impl Frame for Gles2Frame { +impl Renderer for Gles2Renderer { type Error = Gles2Error; type Texture = Gles2Texture; - fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error> { + #[cfg(feature = "wayland_frontend")] + fn shm_formats(&self) -> &[wl_shm::Format] { + &[ + wl_shm::Format::Abgr8888, + wl_shm::Format::Xbgr8888, + wl_shm::Format::Argb8888, + wl_shm::Format::Xrgb8888, + ] + } + + #[cfg(feature = "wayland_frontend")] + fn import_shm(&mut self, buffer: &wl_buffer::WlBuffer) -> Result { + use crate::wayland::shm::with_buffer_contents; + + with_buffer_contents(&buffer, |slice, data| { + self.make_current()?; + + let offset = data.offset as i32; + let width = data.width as i32; + let height = data.height as i32; + let stride = data.stride as i32; + + // number of bytes per pixel + // TODO: compute from data.format + let pixelsize = 4i32; + + // ensure consistency, the SHM handler of smithay should ensure this + assert!((offset + (height - 1) * stride + width * pixelsize) as usize <= slice.len()); + + let (gl_format, shader_idx) = match data.format { + wl_shm::Format::Abgr8888 => (ffi::RGBA, 0), + wl_shm::Format::Xbgr8888 => (ffi::RGBA, 1), + wl_shm::Format::Argb8888 => (ffi::BGRA_EXT, 2), + wl_shm::Format::Xrgb8888 => (ffi::BGRA_EXT, 3), + format => return Err(Gles2Error::UnsupportedPixelFormat(format)), + }; + + let mut tex = 0; + unsafe { + self.gl.GenTextures(1, &mut tex); + self.gl.BindTexture(ffi::TEXTURE_2D, tex); + + self.gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::CLAMP_TO_EDGE as i32); + self.gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_T, ffi::CLAMP_TO_EDGE as i32); + self.gl.PixelStorei(ffi::UNPACK_ROW_LENGTH, stride / pixelsize); + self.gl.TexImage2D(ffi::TEXTURE_2D, 0, gl_format as i32, width, height, 0, gl_format, ffi::UNSIGNED_BYTE as u32, slice.as_ptr() as *const _); + + self.gl.PixelStorei(ffi::UNPACK_ROW_LENGTH, 0); + self.gl.BindTexture(ffi::TEXTURE_2D, 0); + } + + let texture = Gles2Texture { + texture: tex, + texture_kind: shader_idx, + is_external: false, + y_inverted: false, + width: width as u32, + height: height as u32, + }; + self.egl.unbind()?; + + Ok(texture) + }).map_err(Gles2Error::BufferAccessError)? + } + + fn begin(&mut self, width: u32, height: u32, transform: Transform) -> Result<(), Gles2Error> { + self.make_current()?; unsafe { - self.internal.gl.ClearColor(color[0], color[1], color[2], color[3]); - self.internal.gl.Clear(ffi::COLOR_BUFFER_BIT); + self.gl.Viewport(0, 0, width as i32, height as i32); + + self.gl.Enable(ffi::BLEND); + self.gl.BlendFunc(ffi::ONE, ffi::ONE_MINUS_SRC_ALPHA); + } + + // output transformation passed in by the user + let mut projection = Matrix3::::identity(); + //projection = projection * Matrix3::from_translation(Vector2::new(width as f32 / 2.0, height as f32 / 2.0)); + projection = projection * transform.matrix(); + //let (transformed_width, transformed_height) = transform.transform_size(width, height); + //projection = projection * Matrix3::from_translation(Vector2::new(-(transformed_width as f32) / 2.0, -(transformed_height as f32) / 2.0)); + + // replicate https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml + // glOrtho(0, width, 0, height, 1, 1); + let mut renderer = Matrix3::::identity(); + let t = Matrix3::::identity(); + let x = 2.0 / (width as f32); + let y = 2.0 / (height as f32); + + // Rotation & Reflection + renderer[0][0] = x * t[0][0]; + renderer[1][0] = x * t[0][1]; + renderer[0][1] = y * -t[1][0]; + renderer[1][1] = y * -t[1][1]; + + //Translation + renderer[2][0] = -(1.0f32.copysign(renderer[0][0] + renderer[1][0])); + renderer[2][1] = -(1.0f32.copysign(renderer[0][1] + renderer[1][1])); + + self.current_projection = Some(projection * renderer); + Ok(()) + } + + fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error> { + self.make_current()?; + unsafe { + self.gl.ClearColor(color[0], color[1], color[2], color[3]); + self.gl.Clear(ffi::COLOR_BUFFER_BIT); } Ok(()) } fn render_texture(&mut self, tex: &Self::Texture, mut matrix: Matrix3, alpha: f32) -> Result<(), Self::Error> { + self.make_current()?; + if self.current_projection.is_none() { + return Err(Gles2Error::UnconstraintRenderingOperation); + } + //apply output transformation - matrix = self.projection * matrix; + matrix = self.current_projection.as_ref().unwrap() * matrix; let target = if tex.is_external { ffi::TEXTURE_EXTERNAL_OES } else { ffi::TEXTURE_2D }; // render unsafe { - self.internal.gl.ActiveTexture(ffi::TEXTURE0); - self.internal.gl.BindTexture(target, tex.texture); - self.internal.gl.TexParameteri(target, ffi::TEXTURE_MIN_FILTER, ffi::LINEAR as i32); - self.internal.gl.UseProgram(self.internal.programs[tex.texture_kind].program); + self.gl.ActiveTexture(ffi::TEXTURE0); + self.gl.BindTexture(target, tex.texture); + self.gl.TexParameteri(target, ffi::TEXTURE_MIN_FILTER, ffi::LINEAR as i32); + self.gl.UseProgram(self.programs[tex.texture_kind].program); - self.internal.gl.Uniform1i(self.internal.programs[tex.texture_kind].uniform_tex, 0); - self.internal.gl.UniformMatrix3fv(self.internal.programs[tex.texture_kind].uniform_matrix, 1, ffi::FALSE, matrix.as_ptr()); - self.internal.gl.Uniform1i(self.internal.programs[tex.texture_kind].uniform_invert_y, if tex.y_inverted { 1 } else { 0 }); - self.internal.gl.Uniform1f(self.internal.programs[tex.texture_kind].uniform_alpha, alpha); + self.gl.Uniform1i(self.programs[tex.texture_kind].uniform_tex, 0); + self.gl.UniformMatrix3fv(self.programs[tex.texture_kind].uniform_matrix, 1, ffi::FALSE, matrix.as_ptr()); + self.gl.Uniform1i(self.programs[tex.texture_kind].uniform_invert_y, if tex.y_inverted { 1 } else { 0 }); + self.gl.Uniform1f(self.programs[tex.texture_kind].uniform_alpha, alpha); - self.internal.gl.VertexAttribPointer(self.internal.programs[tex.texture_kind].attrib_position as u32, 2, ffi::FLOAT, ffi::FALSE, 0, VERTS.as_ptr() as *const _); - self.internal.gl.VertexAttribPointer(self.internal.programs[tex.texture_kind].attrib_tex_coords as u32, 2, ffi::FLOAT, ffi::FALSE, 0, TEX_COORDS.as_ptr() as *const _); + self.gl.VertexAttribPointer(self.programs[tex.texture_kind].attrib_position as u32, 2, ffi::FLOAT, ffi::FALSE, 0, VERTS.as_ptr() as *const _); + self.gl.VertexAttribPointer(self.programs[tex.texture_kind].attrib_tex_coords as u32, 2, ffi::FLOAT, ffi::FALSE, 0, TEX_COORDS.as_ptr() as *const _); - self.internal.gl.EnableVertexAttribArray(self.internal.programs[tex.texture_kind].attrib_position as u32); - self.internal.gl.EnableVertexAttribArray(self.internal.programs[tex.texture_kind].attrib_tex_coords as u32); + self.gl.EnableVertexAttribArray(self.programs[tex.texture_kind].attrib_position as u32); + self.gl.EnableVertexAttribArray(self.programs[tex.texture_kind].attrib_tex_coords as u32); - self.internal.gl.DrawArrays(ffi::TRIANGLE_STRIP, 0, 4); + self.gl.DrawArrays(ffi::TRIANGLE_STRIP, 0, 4); - self.internal.gl.DisableVertexAttribArray(self.internal.programs[tex.texture_kind].attrib_position as u32); - self.internal.gl.DisableVertexAttribArray(self.internal.programs[tex.texture_kind].attrib_tex_coords as u32); + self.gl.DisableVertexAttribArray(self.programs[tex.texture_kind].attrib_position as u32); + self.gl.DisableVertexAttribArray(self.programs[tex.texture_kind].attrib_tex_coords as u32); - self.internal.gl.BindTexture(target, 0); + self.gl.BindTexture(target, 0); } Ok(()) } - fn finish(self) -> Result<(), crate::backend::SwapBuffersError> { + fn finish(&mut self) -> Result<(), crate::backend::SwapBuffersError> { + self.make_current()?; unsafe { - self.internal.gl.Flush(); - self.internal.gl.Disable(ffi::BLEND); + self.gl.Flush(); + self.gl.Disable(ffi::BLEND); } + self.current_projection = None; + Ok(()) } } diff --git a/src/backend/renderer/mod.rs b/src/backend/renderer/mod.rs index 37339b6..35de5e8 100644 --- a/src/backend/renderer/mod.rs +++ b/src/backend/renderer/mod.rs @@ -133,10 +133,6 @@ pub trait Texture { pub trait Renderer { type Error: Error; type Texture: Texture; - type Frame: Frame; - - #[must_use] - fn begin(&mut self, width: u32, height: u32, transform: Transform) -> Result; #[cfg(feature = "wayland_frontend")] fn shm_formats(&self) -> &[wl_shm::Format] { @@ -144,13 +140,11 @@ pub trait Renderer { &[wl_shm::Format::Argb8888, wl_shm::Format::Xrgb8888] } #[cfg(feature = "wayland_frontend")] - fn import_shm(&self, buffer: &wl_buffer::WlBuffer) -> Result; -} - -pub trait Frame { - type Error: Error; - type Texture: Texture; - + fn import_shm(&mut self, buffer: &wl_buffer::WlBuffer) -> Result; + + fn begin(&mut self, width: u32, height: u32, transform: Transform) -> Result<(), ::Error>; + fn finish(&mut self) -> Result<(), SwapBuffersError>; + fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error>; fn render_texture(&mut self, texture: &Self::Texture, matrix: Matrix3, alpha: f32) -> Result<(), Self::Error>; fn render_texture_at(&mut self, texture: &Self::Texture, pos: (i32, i32), transform: Transform, alpha: f32) -> Result<(), Self::Error> { @@ -173,5 +167,4 @@ pub trait Frame { self.render_texture(texture, mat, alpha) } - fn finish(self) -> Result<(), SwapBuffersError>; } \ No newline at end of file diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 08c2d81..b1450ad 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -4,8 +4,8 @@ use crate::backend::egl::display::EGLDisplay; use crate::backend::{ egl::{context::GlAttributes, native, EGLContext, EGLSurface, Error as EGLError}, renderer::{ - Renderer, Bind, Transform, Frame, - gles2::{Gles2Renderer, Gles2Error, Gles2Texture, Gles2Frame}, + Renderer, Bind, Transform, + gles2::{Gles2Renderer, Gles2Error, Gles2Texture}, }, input::{ Axis, AxisSource, Event as BackendEvent, InputBackend, InputEvent, KeyState, KeyboardKeyEvent, @@ -73,11 +73,6 @@ pub struct WinitGraphicsBackend { size: Rc>, } -pub struct WinitFrame { - frame: Gles2Frame, - egl: Rc, -} - /// Abstracted event loop of a [`WinitWindow`] implementing the [`InputBackend`] trait /// /// You need to call [`dispatch_new_events`](InputBackend::dispatch_new_events) @@ -172,6 +167,8 @@ where } else { unreachable!("No backends for winit other then Wayland and X11 are supported") }; + + context.unbind(); ( display, @@ -187,13 +184,14 @@ where let window = Rc::new(winit_window); let egl = Rc::new(surface); + let mut renderer = unsafe { Gles2Renderer::new(context, log.clone())? }; Ok(( WinitGraphicsBackend { window: window.clone(), display, egl: egl.clone(), - renderer: Gles2Renderer::new(context, log.clone())?, + renderer, size: size.clone(), }, WinitInputBackend { @@ -247,55 +245,46 @@ impl WinitGraphicsBackend { &*self.window } - pub fn begin(&mut self) -> Result { + pub fn begin(&mut self) -> Result<(), Gles2Error> { let (width, height) = { let size = self.size.borrow(); size.physical_size.into() }; - Renderer::begin(self, width, height, Transform::Normal) + + self.renderer.bind(self.egl.clone())?; + self.renderer.begin(width, height, Transform::Normal) } } impl Renderer for WinitGraphicsBackend { type Error = Gles2Error; type Texture = Gles2Texture; - type Frame = WinitFrame; - - fn begin(&mut self, width: u32, height: u32, transform: Transform) -> Result { - self.renderer.bind(&*self.egl)?; - let frame = self.renderer.begin(width, height, transform)?; - - Ok(WinitFrame { - frame, - egl: self.egl.clone(), - }) - } #[cfg(feature = "wayland_frontend")] fn shm_formats(&self) -> &[wl_shm::Format] { - self.renderer.shm_formats() + Renderer::shm_formats(&self.renderer) } #[cfg(feature = "wayland_frontend")] - fn import_shm(&self, buffer: &wl_buffer::WlBuffer) -> Result { + fn import_shm(&mut self, buffer: &wl_buffer::WlBuffer) -> Result { self.renderer.import_shm(buffer) } -} -impl Frame for WinitFrame { - type Error = Gles2Error; - type Texture = Gles2Texture; + fn begin(&mut self, width: u32, height: u32, transform: Transform) -> Result<(), ::Error> { + self.renderer.bind(self.egl.clone())?; + self.renderer.begin(width, height, transform) + } fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error> { - self.frame.clear(color) + self.renderer.clear(color) } fn render_texture(&mut self, texture: &Self::Texture, matrix: Matrix3, alpha: f32) -> Result<(), Self::Error> { - self.frame.render_texture(texture, matrix, alpha) + self.renderer.render_texture(texture, matrix, alpha) } - fn finish(self) -> Result<(), crate::backend::SwapBuffersError> { - self.frame.finish()?; + fn finish(&mut self) -> Result<(), crate::backend::SwapBuffersError> { + self.renderer.finish()?; self.egl.swap_buffers()?; Ok(()) }