diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index eb5c736..2552e04 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -168,24 +168,57 @@ pub fn run_winit(log: Logger) { .and_then(|_| { backend .renderer() - .render(size, Transform::Flipped180, |renderer, frame| { - render_layers_and_windows( - renderer, - frame, - &*state.window_map.borrow(), - output_geometry, - output_scale, - &log, - )?; + .render( + output_geometry + .size + .to_f64() + .to_physical(output_scale as f64) + .to_i32_round(), + Transform::Flipped180, + |renderer, frame| { + render_layers_and_windows( + renderer, + frame, + &*state.window_map.borrow(), + output_geometry, + output_scale, + &log, + )?; - let (x, y) = state.pointer_location.into(); + let (x, y) = state.pointer_location.into(); - // draw the dnd icon if any - { - let guard = state.dnd_icon.lock().unwrap(); - if let Some(ref surface) = *guard { - if surface.as_ref().is_alive() { - draw_dnd_icon( + // draw the dnd icon if any + { + let guard = state.dnd_icon.lock().unwrap(); + if let Some(ref surface) = *guard { + if surface.as_ref().is_alive() { + draw_dnd_icon( + renderer, + frame, + surface, + (x as i32, y as i32).into(), + output_scale, + &log, + )?; + } + } + } + // draw the cursor as relevant + { + let mut guard = state.cursor_status.lock().unwrap(); + // reset the cursor if the surface is no longer alive + let mut reset = false; + if let CursorImageStatus::Image(ref surface) = *guard { + reset = !surface.as_ref().is_alive(); + } + if reset { + *guard = CursorImageStatus::Default; + } + + // draw as relevant + if let CursorImageStatus::Image(ref surface) = *guard { + cursor_visible = false; + draw_cursor( renderer, frame, surface, @@ -193,52 +226,27 @@ pub fn run_winit(log: Logger) { output_scale, &log, )?; + } else { + cursor_visible = true; } } - } - // draw the cursor as relevant - { - let mut guard = state.cursor_status.lock().unwrap(); - // reset the cursor if the surface is no longer alive - let mut reset = false; - if let CursorImageStatus::Image(ref surface) = *guard { - reset = !surface.as_ref().is_alive(); - } - if reset { - *guard = CursorImageStatus::Default; - } - // draw as relevant - if let CursorImageStatus::Image(ref surface) = *guard { - cursor_visible = false; - draw_cursor( + #[cfg(feature = "debug")] + { + let fps = state.backend_data.fps.avg().round() as u32; + + draw_fps( renderer, frame, - surface, - (x as i32, y as i32).into(), - output_scale, - &log, + &state.backend_data.fps_texture, + output_scale as f64, + fps, )?; - } else { - cursor_visible = true; } - } - #[cfg(feature = "debug")] - { - let fps = state.backend_data.fps.avg().round() as u32; - - draw_fps( - renderer, - frame, - &state.backend_data.fps_texture, - output_scale as f64, - fps, - )?; - } - - Ok(()) - }) + Ok(()) + }, + ) .map_err(Into::::into) .and_then(|x| x) }) diff --git a/src/backend/egl/context.rs b/src/backend/egl/context.rs index 8e77b6b..f57cbb3 100644 --- a/src/backend/egl/context.rs +++ b/src/backend/egl/context.rs @@ -1,12 +1,21 @@ //! EGL context related structs -use std::collections::HashSet; -use std::os::raw::c_int; -use std::sync::atomic::Ordering; +use std::{ + collections::HashSet, + os::raw::c_int, + sync::{atomic::Ordering, Arc}, +}; use super::{ffi, wrap_egl_call, Error, MakeCurrentError}; -use crate::backend::allocator::Format as DrmFormat; -use crate::backend::egl::display::{EGLDisplay, PixelFormat}; -use crate::backend::egl::EGLSurface; +use crate::{ + backend::{ + allocator::Format as DrmFormat, + egl::{ + display::{EGLDisplay, PixelFormat}, + EGLSurface, + }, + }, + utils::user_data::UserDataMap, +}; use slog::{info, o, trace}; @@ -17,6 +26,7 @@ pub struct EGLContext { pub(crate) display: EGLDisplay, config_id: ffi::egl::types::EGLConfig, pixel_format: Option, + user_data: Arc, } // EGLContexts can be moved between threads safely unsafe impl Send for EGLContext {} @@ -162,6 +172,11 @@ impl EGLContext { display: display.clone(), config_id, pixel_format, + user_data: if let Some(shared) = shared { + shared.user_data.clone() + } else { + Arc::new(UserDataMap::default()) + }, }) } @@ -241,6 +256,21 @@ impl EGLContext { pub fn dmabuf_texture_formats(&self) -> &HashSet { &self.display.dmabuf_import_formats } + + /// Retrieve user_data associated with this context + /// + /// *Note:* UserData is shared between shared context, if constructed with + /// [`new_shared`](EGLContext::new_shared) or [`new_shared_with_config`](EGLContext::new_shared_with_config). + pub fn user_data(&self) -> &UserDataMap { + &*self.user_data + } + + /// Get a raw handle to the underlying context. + /// + /// The pointer will become invalid, when this struct is destroyed. + pub fn get_context_handle(&self) -> ffi::egl::types::EGLContext { + self.context + } } impl Drop for EGLContext { diff --git a/src/backend/egl/device.rs b/src/backend/egl/device.rs index 5f8b4e0..b375d09 100644 --- a/src/backend/egl/device.rs +++ b/src/backend/egl/device.rs @@ -173,7 +173,9 @@ impl EGLDevice { } /// Returns the pointer to the raw [`EGLDevice`]. - pub fn inner(&self) -> *const c_void { + /// + /// The pointer will become invalid, when this struct is destroyed. + pub fn get_device_handle(&self) -> *const c_void { self.inner } } diff --git a/src/backend/egl/surface.rs b/src/backend/egl/surface.rs index 20f5113..a4a2c24 100644 --- a/src/backend/egl/surface.rs +++ b/src/backend/egl/surface.rs @@ -172,6 +172,16 @@ impl EGLSurface { pub fn resize(&self, width: i32, height: i32, dx: i32, dy: i32) -> bool { self.native.resize(width, height, dx, dy) } + + /// Get a raw handle to the underlying surface + /// + /// *Note*: The surface might get dynamically recreated during swap-buffers + /// causing the pointer to become invalid. + /// + /// The pointer will become invalid, when this struct is destroyed. + pub fn get_surface_handle(&self) -> ffi::egl::types::EGLSurface { + self.surface.load(Ordering::SeqCst) + } } impl Drop for EGLSurface { diff --git a/src/backend/renderer/gles2/mod.rs b/src/backend/renderer/gles2/mod.rs index 7b73dee..adad36f 100644 --- a/src/backend/renderer/gles2/mod.rs +++ b/src/backend/renderer/gles2/mod.rs @@ -178,6 +178,8 @@ pub struct Gles2Renderer { // This field is only accessed if the image or wayland_frontend features are active #[allow(dead_code)] destruction_callback_sender: Sender, + min_filter: TextureFilter, + max_filter: TextureFilter, logger_ptr: Option<*mut ::slog::Logger>, logger: ::slog::Logger, _not_send: *mut (), @@ -186,11 +188,14 @@ pub struct Gles2Renderer { /// Handle to the currently rendered frame during [`Gles2Renderer::render`](Renderer::render) pub struct Gles2Frame { current_projection: Matrix3, + transform: Transform, gl: ffi::Gles2, tex_programs: [Gles2TexProgram; shaders::FRAGMENT_COUNT], solid_program: Gles2SolidProgram, vbos: [ffi::types::GLuint; 2], size: Size, + min_filter: TextureFilter, + max_filter: TextureFilter, } impl fmt::Debug for Gles2Frame { @@ -200,6 +205,8 @@ impl fmt::Debug for Gles2Frame { .field("tex_programs", &self.tex_programs) .field("solid_program", &self.solid_program) .field("size", &self.size) + .field("min_filter", &self.min_filter) + .field("max_filter", &self.max_filter) .finish_non_exhaustive() } } @@ -215,6 +222,8 @@ impl fmt::Debug for Gles2Renderer { .field("solid_program", &self.solid_program) // ffi::Gles2 does not implement Debug .field("egl", &self.egl) + .field("min_filter", &self.min_filter) + .field("max_filter", &self.max_filter) .field("logger", &self.logger) .finish() } @@ -521,7 +530,7 @@ impl Gles2Renderer { gl.BindBuffer(ffi::ARRAY_BUFFER, 0); let (tx, rx) = channel(); - let mut renderer = Gles2Renderer { + let renderer = Gles2Renderer { gl, egl: context, #[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))] @@ -537,12 +546,12 @@ impl Gles2Renderer { destruction_callback: rx, destruction_callback_sender: tx, vbos, + min_filter: TextureFilter::Nearest, + max_filter: TextureFilter::Linear, logger_ptr, logger: log, _not_send: std::ptr::null_mut(), }; - renderer.downscale_filter(TextureFilter::Nearest)?; - renderer.upscale_filter(TextureFilter::Linear)?; renderer.egl.unbind()?; Ok(renderer) } @@ -635,7 +644,6 @@ impl ImportShm for Gles2Renderer { unsafe { self.gl.BindTexture(ffi::TEXTURE_2D, texture.0.texture); - self.gl .TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::CLAMP_TO_EDGE as i32); self.gl @@ -1017,12 +1025,25 @@ impl Drop for Gles2Renderer { } impl Gles2Renderer { + /// Get access to the underlying [`EGLContext`]. + /// + /// *Note*: Modifying the context state, might result in rendering issues. + /// The context state is considerd an implementation detail + /// and no guarantee is made about what can or cannot be changed. + /// To make sure a certain modification does not interfere with + /// the renderer's behaviour, check the source. + pub fn egl_context(&self) -> &EGLContext { + &self.egl + } + /// Run custom code in the GL context owned by this renderer. /// - /// *Note*: Any changes to the GL state should be restored at the end of this function. - /// Otherwise this can lead to rendering errors while using functions of this renderer. - /// Relying on any state set by the renderer may break on any smithay update as the - /// details about how this renderer works are considered an implementation detail. + /// The OpenGL state of the renderer is considered an implementation detail + /// and no guarantee is made about what can or cannot be changed, + /// as such you should reset everything you change back to its previous value + /// or check the source code of the version of Smithay you are using to ensure + /// your changes don't interfere with the renderer's behavior. + /// Doing otherwise can lead to rendering errors while using other functions of this renderer. pub fn with_context(&mut self, func: F) -> Result where F: FnOnce(&mut Self, &ffi::Gles2) -> R, @@ -1039,31 +1060,11 @@ impl Renderer for Gles2Renderer { type Frame = Gles2Frame; fn downscale_filter(&mut self, filter: TextureFilter) -> Result<(), Self::Error> { - self.make_current()?; - unsafe { - self.gl.TexParameteri( - ffi::TEXTURE_2D, - ffi::TEXTURE_MIN_FILTER, - match filter { - TextureFilter::Nearest => ffi::NEAREST as i32, - TextureFilter::Linear => ffi::LINEAR as i32, - }, - ); - } + self.min_filter = filter; Ok(()) } fn upscale_filter(&mut self, filter: TextureFilter) -> Result<(), Self::Error> { - self.make_current()?; - unsafe { - self.gl.TexParameteri( - ffi::TEXTURE_2D, - ffi::TEXTURE_MAG_FILTER, - match filter { - TextureFilter::Nearest => ffi::NEAREST as i32, - TextureFilter::Linear => ffi::LINEAR as i32, - }, - ); - } + self.max_filter = filter; Ok(()) } @@ -1116,8 +1117,11 @@ impl Renderer for Gles2Renderer { solid_program: self.solid_program.clone(), // output transformation passed in by the user current_projection: flip180 * transform.matrix() * renderer, + transform, vbos: self.vbos, size, + min_filter: self.min_filter, + max_filter: self.max_filter, }; let result = rendering(self, &mut frame); @@ -1305,6 +1309,10 @@ impl Frame for Gles2Frame { self.render_texture(texture, mat, Some(&damage), tex_verts, alpha) } + + fn transformation(&self) -> Transform { + self.transform + } } impl Gles2Frame { @@ -1331,8 +1339,22 @@ impl Gles2Frame { unsafe { self.gl.ActiveTexture(ffi::TEXTURE0); self.gl.BindTexture(target, tex.0.texture); - self.gl - .TexParameteri(target, ffi::TEXTURE_MIN_FILTER, ffi::LINEAR as i32); + self.gl.TexParameteri( + ffi::TEXTURE_2D, + ffi::TEXTURE_MIN_FILTER, + match self.min_filter { + TextureFilter::Nearest => ffi::NEAREST as i32, + TextureFilter::Linear => ffi::LINEAR as i32, + }, + ); + self.gl.TexParameteri( + ffi::TEXTURE_2D, + ffi::TEXTURE_MAG_FILTER, + match self.max_filter { + TextureFilter::Nearest => ffi::NEAREST as i32, + TextureFilter::Linear => ffi::LINEAR as i32, + }, + ); self.gl.UseProgram(self.tex_programs[tex.0.texture_kind].program); self.gl @@ -1415,4 +1437,9 @@ impl Gles2Frame { Ok(()) } + + /// Projection matrix for this frame + pub fn projection(&self) -> &[f32; 9] { + self.current_projection.as_ref() + } } diff --git a/src/backend/renderer/mod.rs b/src/backend/renderer/mod.rs index c84c233..b624697 100644 --- a/src/backend/renderer/mod.rs +++ b/src/backend/renderer/mod.rs @@ -108,6 +108,36 @@ impl Transform { size } } + + /// Transforms a rectangle inside an area of a given size by applying this transformation + pub fn transform_rect_in( + &self, + rect: Rectangle, + area: &Size, + ) -> Rectangle { + let size = self.transform_size(rect.size); + + let loc = match *self { + Transform::Normal => rect.loc, + Transform::_90 => (area.h - rect.loc.y - rect.size.h, rect.loc.x).into(), + Transform::_180 => ( + area.w - rect.loc.x - rect.size.w, + area.h - rect.loc.y - rect.size.h, + ) + .into(), + Transform::_270 => (rect.loc.y, area.w - rect.loc.x - rect.size.w).into(), + Transform::Flipped => (area.w - rect.loc.x - rect.size.w, rect.loc.y).into(), + Transform::Flipped90 => (rect.loc.y, rect.loc.x).into(), + Transform::Flipped180 => (rect.loc.x, area.h - rect.loc.y - rect.size.h).into(), + Transform::Flipped270 => ( + area.h - rect.loc.y - rect.size.h, + area.w - rect.loc.x - rect.size.w, + ) + .into(), + }; + + Rectangle::from_loc_and_size(loc, size) + } } #[cfg(feature = "wayland_frontend")] @@ -219,6 +249,9 @@ pub trait Frame { src_transform: Transform, alpha: f32, ) -> Result<(), Self::Error>; + + /// Output transformation that is applied to this frame + fn transformation(&self) -> Transform; } /// Abstraction of commonly used rendering operations for compositors. @@ -527,3 +560,102 @@ pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option::from_loc_and_size((10, 20), (30, 40)); + let size = Size::from((70, 90)); + let transform = Transform::Normal; + + assert_eq!(rect, transform.transform_rect_in(rect, &size)) + } + + #[test] + fn transform_rect_90() { + let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); + let size = Size::from((70, 90)); + let transform = Transform::_90; + + assert_eq!( + Rectangle::from_loc_and_size((30, 10), (40, 30)), + transform.transform_rect_in(rect, &size) + ) + } + + #[test] + fn transform_rect_180() { + let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); + let size = Size::from((70, 90)); + let transform = Transform::_180; + + assert_eq!( + Rectangle::from_loc_and_size((30, 30), (30, 40)), + transform.transform_rect_in(rect, &size) + ) + } + + #[test] + fn transform_rect_270() { + let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); + let size = Size::from((70, 90)); + let transform = Transform::_270; + + assert_eq!( + Rectangle::from_loc_and_size((20, 30), (40, 30)), + transform.transform_rect_in(rect, &size) + ) + } + + #[test] + fn transform_rect_f() { + let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); + let size = Size::from((70, 90)); + let transform = Transform::Flipped; + + assert_eq!( + Rectangle::from_loc_and_size((30, 20), (30, 40)), + transform.transform_rect_in(rect, &size) + ) + } + + #[test] + fn transform_rect_f90() { + let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); + let size = Size::from((70, 80)); + let transform = Transform::Flipped90; + + assert_eq!( + Rectangle::from_loc_and_size((20, 10), (40, 30)), + transform.transform_rect_in(rect, &size) + ) + } + + #[test] + fn transform_rect_f180() { + let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); + let size = Size::from((70, 90)); + let transform = Transform::Flipped180; + + assert_eq!( + Rectangle::from_loc_and_size((10, 30), (30, 40)), + transform.transform_rect_in(rect, &size) + ) + } + + #[test] + fn transform_rect_f270() { + let rect = Rectangle::::from_loc_and_size((10, 20), (30, 40)); + let size = Size::from((70, 90)); + let transform = Transform::Flipped270; + + assert_eq!( + Rectangle::from_loc_and_size((30, 30), (40, 30)), + transform.transform_rect_in(rect, &size) + ) + } +} diff --git a/wlcs_anvil/src/renderer.rs b/wlcs_anvil/src/renderer.rs index 0ceb21d..bd75ec1 100644 --- a/wlcs_anvil/src/renderer.rs +++ b/wlcs_anvil/src/renderer.rs @@ -112,6 +112,10 @@ impl Frame for DummyFrame { ) -> Result<(), Self::Error> { Ok(()) } + + fn transformation(&self) -> Transform { + Transform::Normal + } } pub struct DummyTexture {