From 7dadd63e359b9e20d81dccc2a2a9c0fd1c193abf Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sun, 11 Jul 2021 20:56:08 +0200 Subject: [PATCH] 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