From a938c2874ffecb9d1b6d9fb009bedb435d179995 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sat, 10 Jul 2021 20:28:18 +0200 Subject: [PATCH 1/7] gles2: Allow usage of raw-gl calls out-of-tree --- src/backend/renderer/gles2/mod.rs | 46 +++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/backend/renderer/gles2/mod.rs b/src/backend/renderer/gles2/mod.rs index 9b3fbf8..e783189 100644 --- a/src/backend/renderer/gles2/mod.rs +++ b/src/backend/renderer/gles2/mod.rs @@ -67,6 +67,35 @@ struct Gles2Program { #[derive(Debug, Clone)] pub struct Gles2Texture(Rc); +impl Gles2Texture { + /// Create a Gles2Texture from a raw gl texture id. + /// + /// This expects the texture to be in RGBA format to be rendered + /// correctly by the `render_texture*`-functions of [`Frame`](super::Frame). + /// It is also expected to not be external or y_inverted. + /// + /// Ownership over the texture is taken by the renderer, you should not free the texture yourself. + /// + /// *Safety*: The renderer cannot make sure `tex` is a valid texture id. + pub unsafe fn from_raw( + renderer: &Gles2Renderer, + tex: ffi::types::GLuint, + width: u32, + height: u32, + ) -> Gles2Texture { + Gles2Texture(Rc::new(Gles2TextureInternal { + texture: tex, + texture_kind: 0, + is_external: false, + y_inverted: false, + width, + height, + egl_images: None, + destruction_callback_sender: renderer.destruction_callback_sender.clone(), + })) + } +} + #[derive(Debug)] struct Gles2TextureInternal { texture: ffi::types::GLuint, @@ -964,6 +993,23 @@ static TEX_COORDS: [ffi::types::GLfloat; 8] = [ 0.0, 1.0, // bottom left ]; +impl Gles2Renderer { + /// 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. + pub fn with_context(&mut self, func: F) -> Result + where + F: FnOnce(&mut Self, &ffi::Gles2) -> R, + { + self.make_current()?; + let gl = self.gl.clone(); + Ok(func(self, &gl)) + } +} + impl Renderer for Gles2Renderer { type Error = Gles2Error; type TextureId = Gles2Texture; From 56f5557f8c1fa31b1d27ed1331f5e6d2d4a4b955 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Wed, 7 Jul 2021 20:49:17 +0200 Subject: [PATCH 2/7] anvil: Move import_bitmap into anvil --- Cargo.toml | 3 +-- anvil/Cargo.toml | 4 +-- anvil/src/udev.rs | 35 +++++++++++++++++++++--- src/backend/renderer/gles2/mod.rs | 44 ------------------------------- src/backend/renderer/mod.rs | 14 ---------- 5 files changed, 34 insertions(+), 66 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5e6c051..78d3cfd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,6 @@ drm = { version = "0.4.0", optional = true } drm-ffi = { version = "0.1.0", optional = true } gbm = { version = "0.6.0", optional = true, default-features = false, features = ["drm-support"] } input = { version = "0.6", default-features = false, features=["libinput_1_14"], optional = true } -image = { version = "0.23.14", default-features = false, optional = true } lazy_static = "1" libc = "0.2.70" libseat= { version = "0.1.1", optional = true } @@ -50,7 +49,7 @@ gl_generator = { version = "0.14", optional = true } pkg-config = { version = "0.3.17", optional = true } [features] -default = ["backend_drm", "backend_gbm", "backend_libinput", "backend_udev", "backend_session_logind", "backend_winit", "image", "renderer_gl", "xwayland", "wayland_frontend", "slog-stdlog"] +default = ["backend_drm", "backend_gbm", "backend_libinput", "backend_udev", "backend_session_logind", "backend_winit", "renderer_gl", "xwayland", "wayland_frontend", "slog-stdlog"] backend_winit = ["winit", "wayland-server/dlopen", "backend_egl", "wayland-egl", "renderer_gl"] backend_drm = ["drm", "drm-ffi"] backend_gbm = ["gbm"] diff --git a/anvil/Cargo.toml b/anvil/Cargo.toml index d321b5b..c99a90d 100644 --- a/anvil/Cargo.toml +++ b/anvil/Cargo.toml @@ -9,7 +9,7 @@ edition = "2018" [dependencies] bitflags = "1.2.1" input = { version = "0.5.0", features = ["udev"], optional = true } -image = { version = "0.23.0", optional = true, default-features = false } +image = { version = "0.23.14", default-features = false, optional = true } rand = "0.7" slog = { version = "2.1.1" } slog-term = "2.8" @@ -36,7 +36,7 @@ gl_generator = "0.14" default = [ "winit", "udev", "logind", "egl", "xwayland" ] egl = [ "smithay/use_system_lib", "smithay/backend_egl" ] winit = [ "smithay/backend_winit" ] -udev = [ "smithay/backend_libinput", "smithay/backend_udev", "smithay/backend_drm", "smithay/backend_gbm", "smithay/backend_egl", "smithay/backend_session", "input", "image", "smithay/image"] +udev = [ "smithay/backend_libinput", "smithay/backend_udev", "smithay/backend_drm", "smithay/backend_gbm", "smithay/backend_egl", "smithay/backend_session", "input", "image" ] logind = [ "smithay/backend_session_logind" ] elogind = ["logind", "smithay/backend_session_elogind" ] libseat = ["smithay/backend_session_libseat" ] diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index f95473c..eb955e7 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -19,7 +19,7 @@ use smithay::{ egl::{EGLContext, EGLDisplay}, libinput::{LibinputInputBackend, LibinputSessionInterface}, renderer::{ - gles2::{Gles2Renderer, Gles2Texture}, + gles2::{Gles2Error, Gles2Renderer, Gles2Texture}, Bind, Frame, Renderer, Transform, }, session::{auto::AutoSession, Session, Signal as SessionSignal}, @@ -469,9 +469,7 @@ impl AnvilState { &self.log, ))); - let pointer_image = renderer - .borrow_mut() - .import_bitmap(&self.backend_data.pointer_image) + let pointer_image = import_bitmap(&mut *renderer.borrow_mut(), &self.backend_data.pointer_image) .expect("Failed to load pointer"); let dev_id = device.device_id(); @@ -801,3 +799,32 @@ fn initial_render(surface: &mut RenderSurface, renderer: &mut Gles2Renderer) -> surface.queue_buffer()?; Ok(()) } + +fn import_bitmap>( + renderer: &mut Gles2Renderer, + image: &image::ImageBuffer, C>, +) -> Result { + use smithay::backend::renderer::gles2::ffi; + + renderer.with_context(|renderer, gl| unsafe { + let mut tex = 0; + gl.GenTextures(1, &mut tex); + gl.BindTexture(ffi::TEXTURE_2D, tex); + gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::CLAMP_TO_EDGE as i32); + gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_T, ffi::CLAMP_TO_EDGE as i32); + gl.TexImage2D( + ffi::TEXTURE_2D, + 0, + ffi::RGBA as i32, + image.width() as i32, + image.height() as i32, + 0, + ffi::RGBA, + ffi::UNSIGNED_BYTE as u32, + image.as_ptr() as *const _, + ); + gl.BindTexture(ffi::TEXTURE_2D, 0); + + Gles2Texture::from_raw(renderer, tex, image.width(), image.height()) + }) +} diff --git a/src/backend/renderer/gles2/mod.rs b/src/backend/renderer/gles2/mod.rs index e783189..9583055 100644 --- a/src/backend/renderer/gles2/mod.rs +++ b/src/backend/renderer/gles2/mod.rs @@ -1015,50 +1015,6 @@ impl Renderer for Gles2Renderer { type TextureId = Gles2Texture; type Frame = Gles2Frame; - #[cfg(feature = "image")] - fn import_bitmap>( - &mut self, - image: &image::ImageBuffer, C>, - ) -> Result { - self.make_current()?; - - 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.TexImage2D( - ffi::TEXTURE_2D, - 0, - ffi::RGBA as i32, - image.width() as i32, - image.height() as i32, - 0, - ffi::RGBA, - ffi::UNSIGNED_BYTE as u32, - image.as_ptr() as *const _, - ); - self.gl.BindTexture(ffi::TEXTURE_2D, 0); - } - - let texture = Gles2Texture(Rc::new(Gles2TextureInternal { - texture: tex, - texture_kind: 0, - is_external: false, - y_inverted: false, - width: image.width(), - height: image.height(), - egl_images: None, - destruction_callback_sender: self.destruction_callback_sender.clone(), - })); - self.egl.unbind()?; - - Ok(texture) - } - fn render( &mut self, size: Size, diff --git a/src/backend/renderer/mod.rs b/src/backend/renderer/mod.rs index 96b78ca..7947920 100644 --- a/src/backend/renderer/mod.rs +++ b/src/backend/renderer/mod.rs @@ -217,20 +217,6 @@ pub trait Renderer { /// Type representing a currently in-progress frame during the [`Renderer::render`]-call type Frame: Frame; - /// Import a given bitmap into the renderer. - /// - /// Returns a texture_id, which can be used with `render_texture(_at)` or implementation-specific functions. - /// - /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it, - /// and needs to be freed by calling `destroy_texture` on this renderer to avoid a resource leak. - /// - /// This operation needs no bound or default rendering target. - #[cfg(feature = "image")] - fn import_bitmap>( - &mut self, - image: &image::ImageBuffer, C>, - ) -> Result; - /// Initialize a rendering context on the current rendering target with given dimensions and transformation. /// /// This function *may* error, if: From b67688e1c8e97914a8f25b464028e6cbb50505f4 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sat, 10 Jul 2021 22:16:59 +0200 Subject: [PATCH 3/7] anvil/udev: Try to load XCursor theme for default cursor --- anvil/Cargo.toml | 4 +- anvil/resources/cursor.rgba | Bin 2816 -> 16384 bytes anvil/resources/cursor2.rgba | Bin 16384 -> 0 bytes anvil/src/cursor.rs | 92 +++++++++++++++++++++++++++++++++++ anvil/src/main.rs | 2 + anvil/src/udev.rs | 37 +++++++++----- 6 files changed, 123 insertions(+), 12 deletions(-) delete mode 100644 anvil/resources/cursor2.rgba create mode 100644 anvil/src/cursor.rs diff --git a/anvil/Cargo.toml b/anvil/Cargo.toml index c99a90d..f00625e 100644 --- a/anvil/Cargo.toml +++ b/anvil/Cargo.toml @@ -9,6 +9,8 @@ edition = "2018" [dependencies] bitflags = "1.2.1" input = { version = "0.5.0", features = ["udev"], optional = true } +thiserror = "1" +xcursor = { version = "0.3.3", optional = true } image = { version = "0.23.14", default-features = false, optional = true } rand = "0.7" slog = { version = "2.1.1" } @@ -36,7 +38,7 @@ gl_generator = "0.14" default = [ "winit", "udev", "logind", "egl", "xwayland" ] egl = [ "smithay/use_system_lib", "smithay/backend_egl" ] winit = [ "smithay/backend_winit" ] -udev = [ "smithay/backend_libinput", "smithay/backend_udev", "smithay/backend_drm", "smithay/backend_gbm", "smithay/backend_egl", "smithay/backend_session", "input", "image" ] +udev = [ "smithay/backend_libinput", "smithay/backend_udev", "smithay/backend_drm", "smithay/backend_gbm", "smithay/backend_egl", "smithay/backend_session", "input", "image", "xcursor" ] logind = [ "smithay/backend_session_logind" ] elogind = ["logind", "smithay/backend_session_elogind" ] libseat = ["smithay/backend_session_libseat" ] diff --git a/anvil/resources/cursor.rgba b/anvil/resources/cursor.rgba index 1bd35092c060a6ac4ff37db714bf26e264ace56e..729c1cc466b85bb2b1e9923d4a0bb7db559df4be 100644 GIT binary patch delta 861 zcmZn=YiMAc7!y8G!5WN=CvHKoC-X2OaZ->udyqLIOi0RdkU2+?IWo*h%FZy$OrF3D zu@p!ePdXV-!b8Og=^i2VBV8Jx`4Kl}t14(8M zGUp3N$V6E?ur{;F{+tmo1B@mw=PX5VWVm7woC>aJ1m`lBE{p>YtWn002mv+#zQ7FN delta 178 zcmWm7I}(C07)4=!5{TjpRPX_DES#}&CAQ8`;5O{D1W4AAJ;J%gcgy3PxVl)(i!o;N zCDvLQ%a6#=iShzTC)z8}kU|Cqmau{x*06ys>|hTClyHC}oS=dl&eSe&{c2;3_*)-T P^AC^q7oN5culsufF=QkM diff --git a/anvil/resources/cursor2.rgba b/anvil/resources/cursor2.rgba deleted file mode 100644 index 729c1cc466b85bb2b1e9923d4a0bb7db559df4be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI1O-NKx6vxx~)X~vYbjq=Ws3dTaLKuP~m=FrJuvI9bL!v{}AX>ICKS>b^7eP@g zaT_j75OiUpE)+i&I*Qt0f{G}SV4*Xkr~kS51{afAtKOS?;mn;m^9KC=-nk#|y0NiQ zw6wHz{|s7TVPP2eq14=N_X8sGPfkv{z->Z=l!K|MDaYaAp%>vFA0K}N{>XxYf{<~5 z`!4vsUN6dk{P6J<4s_Ys*&*S8`N8e2udgTEe|A9A)6;cu5T(=UH19y+uc)Y?q@*O8 znVDe)e)P}az*JmZ9FAVIP2qPsodig-SS&O#F~JUKc6PQIIfKn+)2su9zpAQA@Uw&X z_;_}3c;uiFIcs8Kq9z>#<7Wp62?;bdHpU7Lutv5bXDliz(wu|e@v{TWe>5^O!VYL| zZmu13GZPyFn_Y&O&2;Gn1jIOssml$n{S z2?wX)X9rARH_#EWAc87NL3vr{!k{R;!iz`}>6hJTrA7XT?5IhhDYqZ}gwvKwzgJ8 zO-&6oH#gJn?yjhVm6eqb;J2owrRmYDss-ccxz1!V1@?n_ygsC2$uE_al>rCJbJ70({!cJoN7xWN?m6NSF$gniM^$s-aemxa!M?q{ zy}7fq^KEZ$kD{Za0}e_{O9|&hG(SJj<2?$&tQ>q!pakBkrz!mW+>h~0o12?ov$C=r zot>R6ETg@>J;2Y;17Ihg&-cBlsVNtfM)i;e|4W&l&;OWpZEfuf*0MXe@-iZIYirBB zzP?T=DJcO5d3kw!DSLR2b)V~A)n`OU_n%f*SKS#I8TY_*5n)C|mzS5{W!Bc#R>9Bf z0`fcua{7nE;kb#S7%3o?{NUifh#Tf0L8eE&eVNXJI#Z-Q$Lu z_ux#}h5ca;zDq?Y>mc50uNjR-u7S9|zP=aT-QBP7J{xffYa#Dqc>6-dj diff --git a/anvil/src/cursor.rs b/anvil/src/cursor.rs new file mode 100644 index 0000000..b5b5e12 --- /dev/null +++ b/anvil/src/cursor.rs @@ -0,0 +1,92 @@ +use std::io::Read; + +use xcursor::{ + parser::{parse_xcursor, Image}, + CursorTheme, +}; + +static FALLBACK_CURSOR_DATA: &[u8] = include_bytes!("../resources/cursor.rgba"); + +pub struct Cursor { + icons: Vec, + size: u32, +} + +impl Cursor { + pub fn load(log: &::slog::Logger) -> Cursor { + let name = std::env::var("XCURSOR_THEME") + .ok() + .unwrap_or_else(|| "default".into()); + let size = std::env::var("XCURSOR_SIZE") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(24); + + let theme = CursorTheme::load(&name); + let icons = load_icon(&theme) + .map_err(|err| slog::warn!(log, "Unable to load xcursor: {}, using fallback cursor", err)) + .unwrap_or_else(|_| { + vec![Image { + size: 32, + width: 64, + height: 64, + xhot: 1, + yhot: 1, + delay: 1, + pixels_rgba: Vec::from(FALLBACK_CURSOR_DATA), + pixels_argb: vec![], //unused + }] + }); + + Cursor { icons, size } + } + + pub fn get_image(&self, scale: u32, millis: u32) -> Image { + let size = self.size * scale; + frame(millis, size, &self.icons) + } +} + +fn nearest_images(size: u32, images: &[Image]) -> impl Iterator { + // Follow the nominal size of the cursor to choose the nearest + let nearest_image = images + .iter() + .min_by_key(|image| (size as i32 - image.size as i32).abs()) + .unwrap(); + + images + .iter() + .filter(move |image| image.width == nearest_image.width && image.height == nearest_image.height) +} + +fn frame(mut millis: u32, size: u32, images: &[Image]) -> Image { + let total = nearest_images(size, images).fold(0, |acc, image| acc + image.delay); + millis %= total; + + for img in nearest_images(size, images) { + if millis < img.delay { + return img.clone(); + } + millis -= img.delay; + } + + unreachable!() +} + +#[derive(thiserror::Error, Debug)] +enum Error { + #[error("Theme has no default cursor")] + NoDefaultCursor, + #[error("Error opening xcursor file: {0}")] + File(#[from] std::io::Error), + #[error("Failed to parse XCursor file")] + Parse, +} + +fn load_icon(theme: &CursorTheme) -> Result, Error> { + let icon_path = theme.load_icon("default").ok_or(Error::NoDefaultCursor)?; + let mut cursor_file = std::fs::File::open(&icon_path)?; + let mut cursor_data = Vec::new(); + cursor_file.read_to_end(&mut cursor_data)?; + parse_xcursor(&cursor_data).ok_or(Error::Parse) +} diff --git a/anvil/src/main.rs b/anvil/src/main.rs index 46183ca..34f1e88 100644 --- a/anvil/src/main.rs +++ b/anvil/src/main.rs @@ -14,6 +14,8 @@ use std::{cell::RefCell, rc::Rc}; use slog::Drain; use smithay::reexports::{calloop::EventLoop, wayland_server::Display}; +#[cfg(feature = "udev")] +mod cursor; mod drawing; mod input_handler; mod shell; diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index eb955e7..ac835d9 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -90,7 +90,7 @@ pub struct UdevData { primary_gpu: Option, backends: HashMap, signaler: Signaler, - pointer_image: ImageBuffer, Vec>, + pointer_image: crate::cursor::Cursor, render_timer: TimerHandle<(u64, crtc::Handle)>, } @@ -122,7 +122,6 @@ pub fn run_udev( /* * Initialize the compositor */ - let pointer_bytes = include_bytes!("../resources/cursor2.rgba"); #[cfg(feature = "egl")] let primary_gpu = primary_gpu(&session.seat()).unwrap_or_default(); @@ -135,7 +134,7 @@ pub fn run_udev( primary_gpu, backends: HashMap::new(), signaler: session_signal.clone(), - pointer_image: ImageBuffer::from_raw(64, 64, pointer_bytes.to_vec()).unwrap(), + pointer_image: crate::cursor::Cursor::load(&log), render_timer: timer.handle(), }; let mut state = AnvilState::init(display.clone(), event_loop.handle(), data, log.clone()); @@ -258,7 +257,7 @@ pub type RenderSurface = GbmBufferedSurface; struct BackendData { _restart_token: SignalToken, surfaces: Rc>>>>, - pointer_image: Gles2Texture, + pointer_images: Vec<(xcursor::parser::Image, Gles2Texture)>, renderer: Rc>, gbm: GbmDevice, registration_token: RegistrationToken, @@ -469,9 +468,6 @@ impl AnvilState { &self.log, ))); - let pointer_image = import_bitmap(&mut *renderer.borrow_mut(), &self.backend_data.pointer_image) - .expect("Failed to load pointer"); - let dev_id = device.device_id(); let handle = self.handle.clone(); let restart_token = self.backend_data.signaler.register(move |signal| match signal { @@ -509,7 +505,7 @@ impl AnvilState { surfaces: backends, renderer, gbm, - pointer_image, + pointer_images: Vec::new(), dev_id, }, ); @@ -608,15 +604,34 @@ impl AnvilState { }; for (&crtc, surface) in to_render_iter { + // TODO get scale from the rendersurface when supporting HiDPI + let frame = self + .backend_data + .pointer_image + .get_image(1 /*scale*/, self.start_time.elapsed().as_millis() as u32); + let renderer = &mut *device_backend.renderer.borrow_mut(); + let pointer_images = &mut device_backend.pointer_images; + let pointer_image = pointer_images + .iter() + .find_map(|(image, texture)| if image == &frame { Some(texture) } else { None }) + .cloned() + .unwrap_or_else(|| { + let image = + ImageBuffer::from_raw(frame.width, frame.height, &*frame.pixels_rgba).unwrap(); + let texture = import_bitmap(renderer, &image).expect("Failed to import cursor bitmap"); + pointer_images.push((frame, texture.clone())); + texture + }); + let result = render_surface( &mut *surface.borrow_mut(), - &mut *device_backend.renderer.borrow_mut(), + renderer, device_backend.dev_id, crtc, &mut *self.window_map.borrow_mut(), &*self.output_map.borrow(), self.pointer_location, - &device_backend.pointer_image, + &pointer_image, &*self.dnd_icon.lock().unwrap(), &mut *self.cursor_status.lock().unwrap(), &self.log, @@ -802,7 +817,7 @@ fn initial_render(surface: &mut RenderSurface, renderer: &mut Gles2Renderer) -> fn import_bitmap>( renderer: &mut Gles2Renderer, - image: &image::ImageBuffer, C>, + image: &ImageBuffer, C>, ) -> Result { use smithay::backend::renderer::gles2::ffi; From 8bd17d71d202ac7fb1cc51120af6b3cdfc643b9f Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sat, 10 Jul 2021 23:50:01 +0200 Subject: [PATCH 4/7] renderer: Support cropping and scaling --- anvil/src/drawing.rs | 6 +-- anvil/src/udev.rs | 3 +- src/backend/renderer/gles2/mod.rs | 30 +++++------ src/backend/renderer/mod.rs | 85 ++++++++++++++++++++++--------- 4 files changed, 79 insertions(+), 45 deletions(-) diff --git a/anvil/src/drawing.rs b/anvil/src/drawing.rs index 913c3c6..821aca7 100644 --- a/anvil/src/drawing.rs +++ b/anvil/src/drawing.rs @@ -155,7 +155,7 @@ where let mut location = *location; if let Some(ref data) = states.data_map.get::>() { let mut data = data.borrow_mut(); - let buffer_scale = data.buffer_scale as f32; + let buffer_scale = data.buffer_scale; if let Some(texture) = data .texture .as_mut() @@ -167,12 +167,12 @@ where let current = states.cached_state.current::(); location += current.location; } - let render_scale = output_scale as f32 / buffer_scale; if let Err(err) = frame.render_texture_at( &texture.texture, location.to_f64().to_physical(output_scale as f64).to_i32_round(), + buffer_scale, + output_scale as f64, Transform::Normal, /* TODO */ - render_scale, 1.0, ) { result = Err(err.into()); diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index ac835d9..ead4f40 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -755,8 +755,9 @@ fn render_surface( .to_f64() .to_physical(output_scale as f64) .to_i32_round(), + 1, + output_scale as f64, Transform::Normal, - output_scale, 1.0, )?; } diff --git a/src/backend/renderer/gles2/mod.rs b/src/backend/renderer/gles2/mod.rs index 9583055..510e9b1 100644 --- a/src/backend/renderer/gles2/mod.rs +++ b/src/backend/renderer/gles2/mod.rs @@ -11,7 +11,7 @@ use std::sync::{ }; use std::{collections::HashSet, os::raw::c_char}; -use cgmath::{prelude::*, Matrix3}; +use cgmath::{prelude::*, Matrix3, Vector2}; mod shaders; mod version; @@ -76,7 +76,9 @@ impl Gles2Texture { /// /// Ownership over the texture is taken by the renderer, you should not free the texture yourself. /// - /// *Safety*: The renderer cannot make sure `tex` is a valid texture id. + /// # Safety + /// + /// The renderer cannot make sure `tex` is a valid texture id. pub unsafe fn from_raw( renderer: &Gles2Renderer, tex: ffi::types::GLuint, @@ -979,20 +981,6 @@ impl Drop for Gles2Renderer { } } -static VERTS: [ffi::types::GLfloat; 8] = [ - 1.0, 0.0, // top right - 0.0, 0.0, // top left - 1.0, 1.0, // bottom right - 0.0, 1.0, // bottom left -]; - -static TEX_COORDS: [ffi::types::GLfloat; 8] = [ - 1.0, 0.0, // top right - 0.0, 0.0, // top left - 1.0, 1.0, // bottom right - 0.0, 1.0, // bottom left -]; - impl Gles2Renderer { /// Run custom code in the GL context owned by this renderer. /// @@ -1085,6 +1073,13 @@ impl Renderer for Gles2Renderer { } } +static VERTS: [ffi::types::GLfloat; 8] = [ + 1.0, 0.0, // top right + 0.0, 0.0, // top left + 1.0, 1.0, // bottom right + 0.0, 1.0, // bottom left +]; + impl Frame for Gles2Frame { type Error = Gles2Error; type TextureId = Gles2Texture; @@ -1102,6 +1097,7 @@ impl Frame for Gles2Frame { &mut self, tex: &Self::TextureId, mut matrix: Matrix3, + tex_coords: [Vector2; 4], alpha: f32, ) -> Result<(), Self::Error> { //apply output transformation @@ -1150,7 +1146,7 @@ impl Frame for Gles2Frame { ffi::FLOAT, ffi::FALSE, 0, - TEX_COORDS.as_ptr() as *const _, + tex_coords.as_ptr() as *const _, // cgmath::Vector2 is marked as repr(C), this cast should be safe ); self.gl diff --git a/src/backend/renderer/mod.rs b/src/backend/renderer/mod.rs index 7947920..dffc150 100644 --- a/src/backend/renderer/mod.rs +++ b/src/backend/renderer/mod.rs @@ -10,16 +10,18 @@ use std::collections::HashSet; use std::error::Error; -use crate::utils::{Physical, Point, Size}; +use crate::utils::{Buffer, Physical, Point, Rectangle, Size}; #[cfg(feature = "wayland_frontend")] -use crate::{utils::Rectangle, wayland::compositor::SurfaceData}; -use cgmath::{prelude::*, Matrix3, Vector2}; +use crate::wayland::compositor::SurfaceData; +use cgmath::{prelude::*, Matrix3, Vector2, Vector3}; #[cfg(feature = "wayland_frontend")] use wayland_server::protocol::{wl_buffer, wl_shm}; #[cfg(feature = "renderer_gl")] pub mod gles2; +#[cfg(feature = "wayland_frontend")] +use crate::backend::allocator::{dmabuf::Dmabuf, Format}; #[cfg(all( feature = "wayland_frontend", feature = "backend_egl", @@ -29,11 +31,6 @@ use crate::backend::egl::{ display::{EGLBufferReader, BUFFER_READER}, Error as EglError, }; -#[cfg(feature = "wayland_frontend")] -use crate::{ - backend::allocator::{dmabuf::Dmabuf, Format}, - utils::Buffer, -}; #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] /// Possible transformations to two-dimensional planes @@ -142,9 +139,9 @@ pub trait Unbind: Renderer { /// A two dimensional texture pub trait Texture { - /// Size of the texture plane (w x h) - fn size(&self) -> (u32, u32) { - (self.width(), self.height()) + /// Size of the texture plane + fn size(&self) -> Size { + Size::from((self.width() as i32, self.height() as i32)) } /// Width of the texture plane @@ -166,34 +163,57 @@ pub trait Frame { /// If called outside this operation may error-out, do nothing or modify future rendering results in any way. fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error>; /// Render a texture to the current target using given projection matrix and alpha. - /// - /// This operation is only valid in between a `begin` and `finish`-call. - /// If called outside this operation may error-out, do nothing or modify future rendering results in any way. + /// The given verticies are used to source the texture. This is mostly useful for cropping the texture. fn render_texture( &mut self, texture: &Self::TextureId, matrix: Matrix3, + tex_coords: [Vector2; 4], alpha: f32, ) -> Result<(), Self::Error>; + /// Render a texture to the current target as a flat 2d-plane at a given - /// position, applying the given transformation with the given alpha value. - /// - /// This operation is only valid in between a `begin` and `finish`-call. - /// If called outside this operation may error-out, do nothing or modify future rendering results in any way. + /// position and applying the given transformation with the given alpha value. fn render_texture_at( &mut self, texture: &Self::TextureId, - pos: Point, + pos: Point, + texture_scale: i32, + output_scale: f64, + transform: Transform, + alpha: f32, + ) -> Result<(), Self::Error> { + self.render_texture_from_to( + texture, + Rectangle::from_loc_and_size(Point::::from((0, 0)), texture.size()), + Rectangle::from_loc_and_size( + pos, + texture + .size() + .to_logical(texture_scale) + .to_f64() + .to_physical(output_scale), + ), + transform, + alpha, + ) + } + + /// Render part of a texture as given by src to the current target into the rectangle described by dest + /// as a flat 2d-plane after applying the given transformations. + fn render_texture_from_to( + &mut self, + texture: &Self::TextureId, + src: Rectangle, + dest: Rectangle, transform: Transform, - scale: f32, alpha: f32, ) -> Result<(), Self::Error> { let mut mat = Matrix3::::identity(); // position and scale - let size = texture.size(); - mat = mat * Matrix3::from_translation(Vector2::new(pos.x as f32, pos.y as f32)); - mat = mat * Matrix3::from_nonuniform_scale(size.0 as f32 * scale, size.1 as f32 * scale); + mat = mat * Matrix3::from_translation(Vector2::new(dest.loc.x as f32, dest.loc.y as f32)); + mat = mat * Matrix3::from_nonuniform_scale(dest.size.w as f32, dest.size.h as f32); //apply surface transformation mat = mat * Matrix3::from_translation(Vector2::new(0.5, 0.5)); @@ -204,7 +224,24 @@ pub trait Frame { mat = mat * transform.invert().matrix(); mat = mat * Matrix3::from_translation(Vector2::new(-0.5, -0.5)); - self.render_texture(texture, mat, alpha) + // this matrix should be regular, we can expect invert to succeed + let tex_size = texture.size(); + let texture_mat = Matrix3::from_nonuniform_scale(tex_size.w as f32, tex_size.h as f32) + .invert() + .unwrap(); + let verts = [ + (texture_mat * Vector3::new((src.loc.x + src.size.w) as f32, src.loc.y as f32, 0.0)).truncate(), // top-right + (texture_mat * Vector3::new(src.loc.x as f32, src.loc.y as f32, 0.0)).truncate(), // top-left + (texture_mat + * Vector3::new( + (src.loc.x + src.size.w) as f32, + (src.loc.y + src.size.h) as f32, + 0.0, + )) + .truncate(), // bottom-right + (texture_mat * Vector3::new(src.loc.x as f32, (src.loc.y + src.size.h) as f32, 0.0)).truncate(), // bottom-left + ]; + self.render_texture(texture, mat, verts, alpha) } } From 00fb4561b78cffa0d22f7fd153449a9a94d6d8f4 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sat, 10 Jul 2021 23:50:57 +0200 Subject: [PATCH 5/7] gles2: Enable scissor test for optimization. Skips unnecessary drawcalls outside of the viewable area. This may happen with textures rendered over the edges of our screen. --- src/backend/renderer/gles2/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/backend/renderer/gles2/mod.rs b/src/backend/renderer/gles2/mod.rs index 510e9b1..0bb8769 100644 --- a/src/backend/renderer/gles2/mod.rs +++ b/src/backend/renderer/gles2/mod.rs @@ -1019,6 +1019,9 @@ impl Renderer for Gles2Renderer { unsafe { self.gl.Viewport(0, 0, size.w, size.h); + self.gl.Scissor(0, 0, size.w, size.h); + self.gl.Enable(ffi::SCISSOR_TEST); + self.gl.Enable(ffi::BLEND); self.gl.BlendFunc(ffi::ONE, ffi::ONE_MINUS_SRC_ALPHA); } From 7dadd63e359b9e20d81dccc2a2a9c0fd1c193abf Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sun, 11 Jul 2021 20:56:08 +0200 Subject: [PATCH 6/7] anvil: Introduce debug feature and draw fps --- anvil/Cargo.toml | 4 +- anvil/resources/numbers.png | Bin 0 -> 1770 bytes anvil/src/drawing.rs | 81 +++++++++++++++++++++++++++++ anvil/src/udev.rs | 100 +++++++++++++++++++++--------------- anvil/src/winit.rs | 39 +++++++++++++- 5 files changed, 179 insertions(+), 45 deletions(-) create mode 100644 anvil/resources/numbers.png diff --git a/anvil/Cargo.toml b/anvil/Cargo.toml index f00625e..14538e0 100644 --- a/anvil/Cargo.toml +++ b/anvil/Cargo.toml @@ -12,6 +12,7 @@ input = { version = "0.5.0", features = ["udev"], optional = true } thiserror = "1" xcursor = { version = "0.3.3", optional = true } image = { version = "0.23.14", default-features = false, optional = true } +fps_ticker = { version = "1.0.0", optional = true } rand = "0.7" slog = { version = "2.1.1" } slog-term = "2.8" @@ -43,4 +44,5 @@ logind = [ "smithay/backend_session_logind" ] elogind = ["logind", "smithay/backend_session_elogind" ] libseat = ["smithay/backend_session_libseat" ] xwayland = [ "smithay/xwayland", "x11rb" ] -test_all_features = ["default"] +debug = [ "fps_ticker", "image/png" ] +test_all_features = ["default", "debug"] diff --git a/anvil/resources/numbers.png b/anvil/resources/numbers.png new file mode 100644 index 0000000000000000000000000000000000000000..bc01532968ec538ef14d6ee569c6fdd0d760d9b6 GIT binary patch literal 1770 zcmZ`)eK_0K9*>&0TAF7q4KC?C)>U)cM7`9@i1wnGtwcqWbqQhKrj1ndMuL?~rmU(@ zHFQ>{Rw;SvtzBvFgOO%w4(12#zxspay_BP$AB8|#%dFp$&46r04I`X`Kz8&P zhl#E0r{4gj9W4Y;JCTu0W0EtIAXIW1g@&Re(LVG@Hz`*!<3&+BU5HT-^L{pxJ#WsT;*#A}ZWfd&Vi_lCNi1zYjvpvC$w%UCizWjA9| zG_EY%FlWZB_V4BAPur4~w=@q`IXq^pX2&uvYoI{CabT2#Gj0W1AC9XS@n@-J`2#0k zCBY`x1LM)>_3UJ|u+xLfuFM#o4`8Wz7WV`Rh}jGc@gP#pgP#@&`b2_p%{yi{I|CN` zRO~nSG9I>1oLxqsMyR_*D=pIW?@d&%zQU`U(LUmJPV6C=&b~)aa2M=D<0YI5lKM_e zJW926kdqNnq(!t&KBS;^m30$&S;MACCHoqDJp!xl>YOvNT@Dy1dG`}vA4k%DbK&>v zO36m_&^o&LU9Dqud|XBQD-n)zj-0oDPQe?=Z4pE-cyx}mUC3(bve8cTr3aSQ_lDj3 zk#XPq8baQ^5p9$5dg(dL<+npm&+v(h-`(WWfuUq=n1@_8YOZGw$KIlTWVW0`o?-p4h z8(ok%fFv_W+W4ZZMh+-VehHMe+-Tv^e`4B$!EPPk*-TX@}h zO2ZqN3c$1nqofBW3g|;(o}dRMMV7ZG*)qsZ+8AiMW-xLo!AYxfSWP1dZd@mt1`d?a zhuYI88?st2W;39j=@#hng6<2@W;wRZkFRY*>Dwql3z-?70RrIp#`{lN zNb+ft{4dxqW?yl3koR@G=<9&q?F*11mEC@Gw;YDtYUXgl7bn(`${rv){!GRUH!<$> z=%H?VQNk2Ok2DN zOfB}CYqfS~RU)>cZ!-A+aL71n4trL>CKwsRqyOVUjoCcE7xie8oJ~c~9|r7byPdBG z$c&odG=qi?UXC#dQ*YMnzAZT3dZIUztWiq*=El4<&pLsD&^-4Ve4gKsnaVgDTU>px zsl|Gp&qb$IN!Xm^;xDTSofaTU+zJukw2P>7Dxj;JJE(gHF;Zd0>{Qi}t)7JpRaw#2 zi%~UJG&q)9@VsPJXjW0e_t-V7+ez%)nVQu6JSRMFSI#M`JM68tUgsy0>(pEBxoAQm zv6{(O@JHIV3tKK`Hx_xZQYHTvS)qBX=xZLEzeF`25 zLDOUoUV?dBh_y+Ux~ep7uyh}bxQRMCakYPTtW-hrUw6C#Ykd%DD-^MG zzQ+&=3Rj#;H#IgBwI@Nuyp_Nk{}Sl0gyMU-vU@SVU;v}C0@glyY$_0Uzd>752$J<< z;GMEtcXSaGs4InroAseXC!hVP2{B#r_6n%W?E^dcZS_WF%Jl zqksL^|0x$ej6wJg&-T&6l<2+^8 do;_WVVcE`1&d*+lz<&u69&$YR_P^ru{{TSbhzS4y literal 0 HcmV?d00001 diff --git a/anvil/src/drawing.rs b/anvil/src/drawing.rs index 821aca7..2063a7c 100644 --- a/anvil/src/drawing.rs +++ b/anvil/src/drawing.rs @@ -2,7 +2,11 @@ use std::{cell::RefCell, sync::Mutex}; +#[cfg(feature = "image")] +use image::{ImageBuffer, Rgba}; use slog::Logger; +#[cfg(feature = "image")] +use smithay::backend::renderer::gles2::{Gles2Error, Gles2Renderer, Gles2Texture}; use smithay::{ backend::{ renderer::{buffer_type, BufferType, Frame, ImportAll, Renderer, Texture, Transform}, @@ -260,3 +264,80 @@ where } draw_surface_tree(renderer, frame, surface, location, output_scale, log) } + +#[cfg(feature = "debug")] +pub static FPS_NUMBERS_PNG: &[u8] = include_bytes!("../resources/numbers.png"); + +#[cfg(feature = "debug")] +pub fn draw_fps( + _renderer: &mut R, + frame: &mut F, + texture: &T, + output_scale: f64, + value: u32, +) -> Result<(), SwapBuffersError> +where + R: Renderer + ImportAll, + F: Frame, + E: std::error::Error + Into, + T: Texture + 'static, +{ + let value_str = value.to_string(); + let mut offset_x = 0f64; + for digit in value_str.chars().map(|d| d.to_digit(10).unwrap()) { + frame + .render_texture_from_to( + texture, + match digit { + 9 => Rectangle::from_loc_and_size((0, 0), (22, 35)), + 6 => Rectangle::from_loc_and_size((22, 0), (22, 35)), + 3 => Rectangle::from_loc_and_size((44, 0), (22, 35)), + 1 => Rectangle::from_loc_and_size((66, 0), (22, 35)), + 8 => Rectangle::from_loc_and_size((0, 35), (22, 35)), + 0 => Rectangle::from_loc_and_size((22, 35), (22, 35)), + 2 => Rectangle::from_loc_and_size((44, 35), (22, 35)), + 7 => Rectangle::from_loc_and_size((0, 70), (22, 35)), + 4 => Rectangle::from_loc_and_size((22, 70), (22, 35)), + 5 => Rectangle::from_loc_and_size((44, 70), (22, 35)), + _ => unreachable!(), + }, + Rectangle::from_loc_and_size((offset_x, 0.0), (22.0 * output_scale, 35.0 * output_scale)), + Transform::Normal, + 1.0, + ) + .map_err(Into::into)?; + offset_x += 24.0 * output_scale; + } + + Ok(()) +} + +#[cfg(feature = "image")] +pub fn import_bitmap>( + renderer: &mut Gles2Renderer, + image: &ImageBuffer, C>, +) -> Result { + use smithay::backend::renderer::gles2::ffi; + + renderer.with_context(|renderer, gl| unsafe { + let mut tex = 0; + gl.GenTextures(1, &mut tex); + gl.BindTexture(ffi::TEXTURE_2D, tex); + gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::CLAMP_TO_EDGE as i32); + gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_T, ffi::CLAMP_TO_EDGE as i32); + gl.TexImage2D( + ffi::TEXTURE_2D, + 0, + ffi::RGBA as i32, + image.width() as i32, + image.height() as i32, + 0, + ffi::RGBA, + ffi::UNSIGNED_BYTE as u32, + image.as_ptr() as *const _, + ); + gl.BindTexture(ffi::TEXTURE_2D, 0); + + Gles2Texture::from_raw(renderer, tex, image.width(), image.height()) + }) +} diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index ead4f40..d1ad5d6 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -9,7 +9,7 @@ use std::{ time::Duration, }; -use image::{ImageBuffer, Rgba}; +use image::ImageBuffer; use slog::Logger; use smithay::{ @@ -19,7 +19,7 @@ use smithay::{ egl::{EGLContext, EGLDisplay}, libinput::{LibinputInputBackend, LibinputSessionInterface}, renderer::{ - gles2::{Gles2Error, Gles2Renderer, Gles2Texture}, + gles2::{Gles2Renderer, Gles2Texture}, Bind, Frame, Renderer, Transform, }, session::{auto::AutoSession, Session, Signal as SessionSignal}, @@ -254,10 +254,18 @@ pub fn run_udev( pub type RenderSurface = GbmBufferedSurface; +struct SurfaceData { + surface: RenderSurface, + #[cfg(feature = "debug")] + fps: fps_ticker::Fps, +} + struct BackendData { _restart_token: SignalToken, - surfaces: Rc>>>>, + surfaces: Rc>>>>, pointer_images: Vec<(xcursor::parser::Image, Gles2Texture)>, + #[cfg(feature = "debug")] + fps_texture: Gles2Texture, renderer: Rc>, gbm: GbmDevice, registration_token: RegistrationToken, @@ -272,7 +280,7 @@ fn scan_connectors( output_map: &mut crate::output_map::OutputMap, signaler: &Signaler, logger: &::slog::Logger, -) -> HashMap>> { +) -> HashMap>> { // Get a set of all modesetting resource handles (excluding planes): let res_handles = device.resource_handles().unwrap(); @@ -321,7 +329,7 @@ fn scan_connectors( let renderer_formats = Bind::::supported_formats(renderer).expect("Dmabuf renderer without formats"); - let renderer = + let gbm_surface = match GbmBufferedSurface::new(surface, gbm.clone(), renderer_formats, logger.clone()) { Ok(renderer) => renderer, @@ -373,7 +381,11 @@ fn scan_connectors( device_id: device.device_id(), }); - entry.insert(Rc::new(RefCell::new(renderer))); + entry.insert(Rc::new(RefCell::new(SurfaceData { + surface: gbm_surface, + #[cfg(feature = "debug")] + fps: fps_ticker::Fps::default(), + }))); break 'outer; } } @@ -496,6 +508,19 @@ impl AnvilState { schedule_initial_render(backend.clone(), renderer.clone(), &self.handle, self.log.clone()); } + #[cfg(feature = "debug")] + let fps_texture = import_bitmap( + &mut renderer.borrow_mut(), + &image::io::Reader::with_format( + std::io::Cursor::new(FPS_NUMBERS_PNG), + image::ImageFormat::Png, + ) + .decode() + .unwrap() + .to_rgba8(), + ) + .expect("Unable to upload FPS texture"); + self.backend_data.backends.insert( dev_id, BackendData { @@ -506,6 +531,8 @@ impl AnvilState { renderer, gbm, pointer_images: Vec::new(), + #[cfg(feature = "debug")] + fps_texture, dev_id, }, ); @@ -596,7 +623,7 @@ impl AnvilState { .iter() .flat_map(|crtc| surfaces.get(&crtc).map(|surface| (crtc, surface))); - let to_render_iter: &mut dyn Iterator>)> = + let to_render_iter: &mut dyn Iterator>)> = if crtc.is_some() { &mut option_iter } else { @@ -632,6 +659,8 @@ impl AnvilState { &*self.output_map.borrow(), self.pointer_location, &pointer_image, + #[cfg(feature = "debug")] + &device_backend.fps_texture, &*self.dnd_icon.lock().unwrap(), &mut *self.cursor_status.lock().unwrap(), &self.log, @@ -671,7 +700,7 @@ impl AnvilState { #[allow(clippy::too_many_arguments)] fn render_surface( - surface: &mut RenderSurface, + surface: &mut SurfaceData, renderer: &mut Gles2Renderer, device_id: dev_t, crtc: crtc::Handle, @@ -679,11 +708,12 @@ fn render_surface( output_map: &crate::output_map::OutputMap, pointer_location: Point, pointer_image: &Gles2Texture, + #[cfg(feature = "debug")] fps_texture: &Gles2Texture, dnd_icon: &Option, cursor_status: &mut CursorImageStatus, logger: &slog::Logger, ) -> Result<(), SwapBuffersError> { - surface.frame_submitted()?; + surface.surface.frame_submitted()?; let output = output_map .find(|o| o.userdata().get::() == Some(&UdevOutputId { device_id, crtc })) @@ -696,7 +726,7 @@ fn render_surface( return Ok(()); }; - let dmabuf = surface.next_buffer()?; + let dmabuf = surface.surface.next_buffer()?; renderer.bind(dmabuf)?; // and draw to our buffer match renderer @@ -763,6 +793,18 @@ fn render_surface( } } } + + #[cfg(feature = "debug")] + { + draw_fps( + renderer, + frame, + fps_texture, + output_scale as f64, + surface.fps.avg().round() as u32, + )?; + surface.fps.tick(); + } Ok(()) }, ) @@ -770,13 +812,16 @@ fn render_surface( .and_then(|x| x) .map_err(Into::::into) { - Ok(()) => surface.queue_buffer().map_err(Into::::into), + Ok(()) => surface + .surface + .queue_buffer() + .map_err(Into::::into), Err(err) => Err(err), } } fn schedule_initial_render( - surface: Rc>, + surface: Rc>, renderer: Rc>, evt_handle: &LoopHandle<'static, Data>, logger: ::slog::Logger, @@ -784,7 +829,7 @@ fn schedule_initial_render( let result = { let mut surface = surface.borrow_mut(); let mut renderer = renderer.borrow_mut(); - initial_render(&mut *surface, &mut *renderer) + initial_render(&mut surface.surface, &mut *renderer) }; if let Err(err) = result { match err { @@ -815,32 +860,3 @@ fn initial_render(surface: &mut RenderSurface, renderer: &mut Gles2Renderer) -> surface.queue_buffer()?; Ok(()) } - -fn import_bitmap>( - renderer: &mut Gles2Renderer, - image: &ImageBuffer, C>, -) -> Result { - use smithay::backend::renderer::gles2::ffi; - - renderer.with_context(|renderer, gl| unsafe { - let mut tex = 0; - gl.GenTextures(1, &mut tex); - gl.BindTexture(ffi::TEXTURE_2D, tex); - gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_S, ffi::CLAMP_TO_EDGE as i32); - gl.TexParameteri(ffi::TEXTURE_2D, ffi::TEXTURE_WRAP_T, ffi::CLAMP_TO_EDGE as i32); - gl.TexImage2D( - ffi::TEXTURE_2D, - 0, - ffi::RGBA as i32, - image.width() as i32, - image.height() as i32, - 0, - ffi::RGBA, - ffi::UNSIGNED_BYTE as u32, - image.as_ptr() as *const _, - ); - gl.BindTexture(ffi::TEXTURE_2D, 0); - - Gles2Texture::from_raw(renderer, tex, image.width(), image.height()) - }) -} diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index 589f1a5..dfe38a7 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -1,5 +1,7 @@ use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration}; +#[cfg(feature = "debug")] +use smithay::backend::renderer::gles2::Gles2Texture; #[cfg(feature = "egl")] use smithay::{ backend::renderer::{ImportDma, ImportEgl}, @@ -24,7 +26,12 @@ use crate::state::{AnvilState, Backend}; pub const OUTPUT_NAME: &str = "winit"; -pub struct WinitData; +pub struct WinitData { + #[cfg(feature = "debug")] + fps_texture: Gles2Texture, + #[cfg(feature = "debug")] + pub fps: fps_ticker::Fps, +} impl Backend for WinitData { fn seat_name(&self) -> String { @@ -71,7 +78,20 @@ pub fn run_winit( * Initialize the globals */ - let mut state = AnvilState::init(display.clone(), event_loop.handle(), WinitData, log.clone()); + let data = WinitData { + #[cfg(feature = "debug")] + fps_texture: import_bitmap( + &mut renderer.borrow_mut().renderer(), + &image::io::Reader::with_format(std::io::Cursor::new(FPS_NUMBERS_PNG), image::ImageFormat::Png) + .decode() + .unwrap() + .to_rgba8(), + ) + .expect("Unable to upload FPS texture"), + #[cfg(feature = "debug")] + fps: fps_ticker::Fps::default(), + }; + let mut state = AnvilState::init(display.clone(), event_loop.handle(), data, log.clone()); let mode = Mode { size, @@ -176,6 +196,18 @@ pub fn run_winit( } } + #[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(()) }) .map_err(Into::::into) @@ -206,6 +238,9 @@ pub fn run_winit( state.window_map.borrow_mut().refresh(); state.output_map.borrow_mut().refresh(); } + + #[cfg(feature = "debug")] + state.backend_data.fps.tick(); } // Cleanup stuff From 1cf1d4739b6c1e4de88fb7db687caa78ee8e8f45 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Tue, 13 Jul 2021 21:11:30 +0200 Subject: [PATCH 7/7] buffer: use Size instead of width/height --- anvil/src/drawing.rs | 6 +++++- src/backend/allocator/dmabuf.rs | 27 ++++++++++++--------------- src/backend/allocator/dumb.rs | 10 ++++------ src/backend/allocator/gbm.rs | 13 +++++++------ src/backend/allocator/mod.rs | 15 +++++++++------ src/backend/egl/display.rs | 3 +-- src/backend/egl/mod.rs | 8 ++++---- src/backend/renderer/gles2/mod.rs | 26 +++++++++++--------------- src/wayland/dmabuf/mod.rs | 14 ++------------ 9 files changed, 55 insertions(+), 67 deletions(-) diff --git a/anvil/src/drawing.rs b/anvil/src/drawing.rs index 2063a7c..dc33336 100644 --- a/anvil/src/drawing.rs +++ b/anvil/src/drawing.rs @@ -338,6 +338,10 @@ pub fn import_bitmap>( ); gl.BindTexture(ffi::TEXTURE_2D, 0); - Gles2Texture::from_raw(renderer, tex, image.width(), image.height()) + Gles2Texture::from_raw( + renderer, + tex, + (image.width() as i32, image.height() as i32).into(), + ) }) } diff --git a/src/backend/allocator/dmabuf.rs b/src/backend/allocator/dmabuf.rs index df2e119..090d160 100644 --- a/src/backend/allocator/dmabuf.rs +++ b/src/backend/allocator/dmabuf.rs @@ -11,6 +11,7 @@ //! the lifetime of the buffer. E.g. when you are only caching associated resources for a dmabuf. use super::{Buffer, Format, Fourcc, Modifier}; +use crate::utils::{Buffer as BufferCoords, Size}; use std::hash::{Hash, Hasher}; use std::os::unix::io::{IntoRawFd, RawFd}; use std::sync::{Arc, Weak}; @@ -22,10 +23,8 @@ pub const MAX_PLANES: usize = 4; pub(crate) struct DmabufInternal { /// The submitted planes pub planes: Vec, - /// The width of this buffer - pub width: i32, - /// The height of this buffer - pub height: i32, + /// The size of this buffer + pub size: Size, /// The format in use pub format: Fourcc, /// The flags applied to it @@ -107,12 +106,8 @@ impl Hash for WeakDmabuf { } impl Buffer for Dmabuf { - fn width(&self) -> u32 { - self.0.width as u32 - } - - fn height(&self) -> u32 { - self.0.height as u32 + fn size(&self) -> Size { + self.0.size } fn format(&self) -> Format { @@ -172,8 +167,7 @@ impl Dmabuf { DmabufBuilder { internal: DmabufInternal { planes: Vec::with_capacity(MAX_PLANES), - width: src.width() as i32, - height: src.height() as i32, + size: src.size(), format: src.format().code, flags, }, @@ -181,12 +175,15 @@ impl Dmabuf { } /// Create a new Dmabuf builder - pub fn builder(width: u32, height: u32, format: Fourcc, flags: DmabufFlags) -> DmabufBuilder { + pub fn builder( + size: impl Into>, + format: Fourcc, + flags: DmabufFlags, + ) -> DmabufBuilder { DmabufBuilder { internal: DmabufInternal { planes: Vec::with_capacity(MAX_PLANES), - width: width as i32, - height: height as i32, + size: size.into(), format, flags, }, diff --git a/src/backend/allocator/dumb.rs b/src/backend/allocator/dumb.rs index 0ee8632..fab167a 100644 --- a/src/backend/allocator/dumb.rs +++ b/src/backend/allocator/dumb.rs @@ -9,6 +9,7 @@ use drm::control::{dumbbuffer::DumbBuffer as Handle, Device as ControlDevice}; use super::{Allocator, Buffer, Format, Fourcc, Modifier}; use crate::backend::drm::device::{DrmDevice, DrmDeviceInternal, FdWrapper}; +use crate::utils::{Buffer as BufferCoords, Size}; /// Wrapper around raw DumbBuffer handles. pub struct DumbBuffer { @@ -61,12 +62,9 @@ impl Allocator> for DrmDevice { } impl Buffer for DumbBuffer { - fn width(&self) -> u32 { - self.handle.size().0 - } - - fn height(&self) -> u32 { - self.handle.size().1 + fn size(&self) -> Size { + let (w, h) = self.handle.size(); + (w as i32, h as i32).into() } fn format(&self) -> Format { diff --git a/src/backend/allocator/gbm.rs b/src/backend/allocator/gbm.rs index 45b6ca2..f5bf9fd 100644 --- a/src/backend/allocator/gbm.rs +++ b/src/backend/allocator/gbm.rs @@ -8,6 +8,7 @@ use super::{ dmabuf::{AsDmabuf, Dmabuf, DmabufFlags, MAX_PLANES}, Allocator, Buffer, Format, Fourcc, Modifier, }; +use crate::utils::{Buffer as BufferCoords, Size}; pub use gbm::{BufferObject as GbmBuffer, BufferObjectFlags as GbmBufferFlags, Device as GbmDevice}; use std::os::unix::io::AsRawFd; @@ -39,12 +40,12 @@ impl Allocator> for GbmDevice { } impl Buffer for GbmBuffer { - fn width(&self) -> u32 { - self.width().unwrap_or(0) - } - - fn height(&self) -> u32 { - self.height().unwrap_or(0) + fn size(&self) -> Size { + ( + self.width().unwrap_or(0) as i32, + self.height().unwrap_or(0) as i32, + ) + .into() } fn format(&self) -> Format { diff --git a/src/backend/allocator/mod.rs b/src/backend/allocator/mod.rs index e9865b3..108f0bf 100644 --- a/src/backend/allocator/mod.rs +++ b/src/backend/allocator/mod.rs @@ -22,6 +22,7 @@ pub mod dumb; pub mod gbm; mod swapchain; +use crate::utils::{Buffer as BufferCoords, Size}; pub use swapchain::{Slot, Swapchain}; pub use drm_fourcc::{ @@ -32,13 +33,15 @@ pub use drm_fourcc::{ /// Common trait describing common properties of most types of buffers. pub trait Buffer { /// Width of the two-dimensional buffer - fn width(&self) -> u32; - /// Height of the two-dimensional buffer - fn height(&self) -> u32; - /// Size (w x h) of the two-dimensional buffer - fn size(&self) -> (u32, u32) { - (self.width(), self.height()) + fn width(&self) -> u32 { + self.size().w as u32 } + /// Height of the two-dimensional buffer + fn height(&self) -> u32 { + self.size().h as u32 + } + /// Size of the two-dimensional buffer + fn size(&self) -> Size; /// Pixel format of the buffer fn format(&self) -> Format; } diff --git a/src/backend/egl/display.rs b/src/backend/egl/display.rs index 8a7da5a..98542a8 100644 --- a/src/backend/egl/display.rs +++ b/src/backend/egl/display.rs @@ -808,8 +808,7 @@ impl EGLBufferReader { Ok(EGLBuffer { display: self.display.clone(), - width: width as u32, - height: height as u32, + size: (width, height).into(), // y_inverted is negated here because the gles2 renderer // already inverts the buffer during rendering. y_inverted: !y_inverted, diff --git a/src/backend/egl/mod.rs b/src/backend/egl/mod.rs index 2cd26b3..746cc67 100644 --- a/src/backend/egl/mod.rs +++ b/src/backend/egl/mod.rs @@ -32,6 +32,8 @@ pub use self::context::EGLContext; mod error; pub use self::error::*; use crate::backend::SwapBuffersError as GraphicsSwapBuffersError; +#[cfg(feature = "wayland_frontend")] +use crate::utils::{Buffer, Size}; use nix::libc::c_void; @@ -248,10 +250,8 @@ impl Format { #[derive(Debug)] pub struct EGLBuffer { display: Arc, - /// Width in pixels - pub width: u32, - /// Height in pixels - pub height: u32, + /// Size of the buffer + pub size: Size, /// If the y-axis is inverted or not pub y_inverted: bool, /// Format of these images diff --git a/src/backend/renderer/gles2/mod.rs b/src/backend/renderer/gles2/mod.rs index 0bb8769..9724b28 100644 --- a/src/backend/renderer/gles2/mod.rs +++ b/src/backend/renderer/gles2/mod.rs @@ -34,7 +34,6 @@ use super::ImportEgl; use super::{ImportDma, ImportShm}; #[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))] use crate::backend::egl::{display::EGLBufferReader, Format as EGLFormat}; -#[cfg(feature = "wayland_frontend")] use crate::utils::{Buffer, Rectangle}; #[cfg(feature = "wayland_frontend")] use wayland_server::protocol::{wl_buffer, wl_shm}; @@ -82,16 +81,14 @@ impl Gles2Texture { pub unsafe fn from_raw( renderer: &Gles2Renderer, tex: ffi::types::GLuint, - width: u32, - height: u32, + size: Size, ) -> Gles2Texture { Gles2Texture(Rc::new(Gles2TextureInternal { texture: tex, texture_kind: 0, is_external: false, y_inverted: false, - width, - height, + size, egl_images: None, destruction_callback_sender: renderer.destruction_callback_sender.clone(), })) @@ -104,8 +101,7 @@ struct Gles2TextureInternal { texture_kind: usize, is_external: bool, y_inverted: bool, - width: u32, - height: u32, + size: Size, egl_images: Option>, destruction_callback_sender: Sender, } @@ -132,10 +128,13 @@ enum CleanupResource { impl Texture for Gles2Texture { fn width(&self) -> u32 { - self.0.width + self.0.size.w as u32 } fn height(&self) -> u32 { - self.0.height + self.0.size.h as u32 + } + fn size(&self) -> Size { + self.0.size } } @@ -590,8 +589,7 @@ impl ImportShm for Gles2Renderer { texture_kind: shader_idx, is_external: false, y_inverted: false, - width: width as u32, - height: height as u32, + size: (width, height).into(), egl_images: None, destruction_callback_sender: self.destruction_callback_sender.clone(), }) @@ -720,8 +718,7 @@ impl ImportEgl for Gles2Renderer { }, is_external: egl.format == EGLFormat::External, y_inverted: egl.y_inverted, - width: egl.width, - height: egl.height, + size: egl.size, egl_images: Some(egl.into_images()), destruction_callback_sender: self.destruction_callback_sender.clone(), })); @@ -754,8 +751,7 @@ impl ImportDma for Gles2Renderer { texture_kind: if is_external { 2 } else { 0 }, is_external, y_inverted: buffer.y_inverted(), - width: buffer.width(), - height: buffer.height(), + size: buffer.size(), egl_images: Some(vec![image]), destruction_callback_sender: self.destruction_callback_sender.clone(), })); diff --git a/src/wayland/dmabuf/mod.rs b/src/wayland/dmabuf/mod.rs index a572ba5..358f720 100644 --- a/src/wayland/dmabuf/mod.rs +++ b/src/wayland/dmabuf/mod.rs @@ -260,12 +260,7 @@ where return; } - let mut buf = Dmabuf::builder( - width as u32, - height as u32, - format, - DmabufFlags::from_bits_truncate(flags), - ); + let mut buf = Dmabuf::builder((width, height), format, DmabufFlags::from_bits_truncate(flags)); let planes = std::mem::take(&mut self.pending_planes); for (i, plane) in planes.into_iter().enumerate() { let offset = plane.offset; @@ -350,12 +345,7 @@ where return; } - let mut buf = Dmabuf::builder( - width as u32, - height as u32, - format, - DmabufFlags::from_bits_truncate(flags), - ); + let mut buf = Dmabuf::builder((width, height), format, DmabufFlags::from_bits_truncate(flags)); let planes = ::std::mem::take(&mut self.pending_planes); for (i, plane) in planes.into_iter().enumerate() { let offset = plane.offset;