anvil: Introduce debug feature and draw fps

This commit is contained in:
Victor Brekenfeld 2021-07-11 20:56:08 +02:00
parent 00fb4561b7
commit 7dadd63e35
5 changed files with 179 additions and 45 deletions

View File

@ -12,6 +12,7 @@ input = { version = "0.5.0", features = ["udev"], optional = true }
thiserror = "1" thiserror = "1"
xcursor = { version = "0.3.3", optional = true } xcursor = { version = "0.3.3", optional = true }
image = { version = "0.23.14", default-features = false, optional = true } image = { version = "0.23.14", default-features = false, optional = true }
fps_ticker = { version = "1.0.0", optional = true }
rand = "0.7" rand = "0.7"
slog = { version = "2.1.1" } slog = { version = "2.1.1" }
slog-term = "2.8" slog-term = "2.8"
@ -43,4 +44,5 @@ logind = [ "smithay/backend_session_logind" ]
elogind = ["logind", "smithay/backend_session_elogind" ] elogind = ["logind", "smithay/backend_session_elogind" ]
libseat = ["smithay/backend_session_libseat" ] libseat = ["smithay/backend_session_libseat" ]
xwayland = [ "smithay/xwayland", "x11rb" ] xwayland = [ "smithay/xwayland", "x11rb" ]
test_all_features = ["default"] debug = [ "fps_ticker", "image/png" ]
test_all_features = ["default", "debug"]

BIN
anvil/resources/numbers.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -2,7 +2,11 @@
use std::{cell::RefCell, sync::Mutex}; use std::{cell::RefCell, sync::Mutex};
#[cfg(feature = "image")]
use image::{ImageBuffer, Rgba};
use slog::Logger; use slog::Logger;
#[cfg(feature = "image")]
use smithay::backend::renderer::gles2::{Gles2Error, Gles2Renderer, Gles2Texture};
use smithay::{ use smithay::{
backend::{ backend::{
renderer::{buffer_type, BufferType, Frame, ImportAll, Renderer, Texture, Transform}, renderer::{buffer_type, BufferType, Frame, ImportAll, Renderer, Texture, Transform},
@ -260,3 +264,80 @@ where
} }
draw_surface_tree(renderer, frame, surface, location, output_scale, log) 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<R, E, F, T>(
_renderer: &mut R,
frame: &mut F,
texture: &T,
output_scale: f64,
value: u32,
) -> Result<(), SwapBuffersError>
where
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportAll,
F: Frame<Error = E, TextureId = T>,
E: std::error::Error + Into<SwapBuffersError>,
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<C: std::ops::Deref<Target = [u8]>>(
renderer: &mut Gles2Renderer,
image: &ImageBuffer<Rgba<u8>, C>,
) -> Result<Gles2Texture, Gles2Error> {
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())
})
}

View File

@ -9,7 +9,7 @@ use std::{
time::Duration, time::Duration,
}; };
use image::{ImageBuffer, Rgba}; use image::ImageBuffer;
use slog::Logger; use slog::Logger;
use smithay::{ use smithay::{
@ -19,7 +19,7 @@ use smithay::{
egl::{EGLContext, EGLDisplay}, egl::{EGLContext, EGLDisplay},
libinput::{LibinputInputBackend, LibinputSessionInterface}, libinput::{LibinputInputBackend, LibinputSessionInterface},
renderer::{ renderer::{
gles2::{Gles2Error, Gles2Renderer, Gles2Texture}, gles2::{Gles2Renderer, Gles2Texture},
Bind, Frame, Renderer, Transform, Bind, Frame, Renderer, Transform,
}, },
session::{auto::AutoSession, Session, Signal as SessionSignal}, session::{auto::AutoSession, Session, Signal as SessionSignal},
@ -254,10 +254,18 @@ pub fn run_udev(
pub type RenderSurface = GbmBufferedSurface<SessionFd>; pub type RenderSurface = GbmBufferedSurface<SessionFd>;
struct SurfaceData {
surface: RenderSurface,
#[cfg(feature = "debug")]
fps: fps_ticker::Fps,
}
struct BackendData { struct BackendData {
_restart_token: SignalToken, _restart_token: SignalToken,
surfaces: Rc<RefCell<HashMap<crtc::Handle, Rc<RefCell<RenderSurface>>>>>, surfaces: Rc<RefCell<HashMap<crtc::Handle, Rc<RefCell<SurfaceData>>>>>,
pointer_images: Vec<(xcursor::parser::Image, Gles2Texture)>, pointer_images: Vec<(xcursor::parser::Image, Gles2Texture)>,
#[cfg(feature = "debug")]
fps_texture: Gles2Texture,
renderer: Rc<RefCell<Gles2Renderer>>, renderer: Rc<RefCell<Gles2Renderer>>,
gbm: GbmDevice<SessionFd>, gbm: GbmDevice<SessionFd>,
registration_token: RegistrationToken, registration_token: RegistrationToken,
@ -272,7 +280,7 @@ fn scan_connectors(
output_map: &mut crate::output_map::OutputMap, output_map: &mut crate::output_map::OutputMap,
signaler: &Signaler<SessionSignal>, signaler: &Signaler<SessionSignal>,
logger: &::slog::Logger, logger: &::slog::Logger,
) -> HashMap<crtc::Handle, Rc<RefCell<RenderSurface>>> { ) -> HashMap<crtc::Handle, Rc<RefCell<SurfaceData>>> {
// Get a set of all modesetting resource handles (excluding planes): // Get a set of all modesetting resource handles (excluding planes):
let res_handles = device.resource_handles().unwrap(); let res_handles = device.resource_handles().unwrap();
@ -321,7 +329,7 @@ fn scan_connectors(
let renderer_formats = let renderer_formats =
Bind::<Dmabuf>::supported_formats(renderer).expect("Dmabuf renderer without formats"); Bind::<Dmabuf>::supported_formats(renderer).expect("Dmabuf renderer without formats");
let renderer = let gbm_surface =
match GbmBufferedSurface::new(surface, gbm.clone(), renderer_formats, logger.clone()) match GbmBufferedSurface::new(surface, gbm.clone(), renderer_formats, logger.clone())
{ {
Ok(renderer) => renderer, Ok(renderer) => renderer,
@ -373,7 +381,11 @@ fn scan_connectors(
device_id: device.device_id(), 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; break 'outer;
} }
} }
@ -496,6 +508,19 @@ impl AnvilState<UdevData> {
schedule_initial_render(backend.clone(), renderer.clone(), &self.handle, self.log.clone()); 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( self.backend_data.backends.insert(
dev_id, dev_id,
BackendData { BackendData {
@ -506,6 +531,8 @@ impl AnvilState<UdevData> {
renderer, renderer,
gbm, gbm,
pointer_images: Vec::new(), pointer_images: Vec::new(),
#[cfg(feature = "debug")]
fps_texture,
dev_id, dev_id,
}, },
); );
@ -596,7 +623,7 @@ impl AnvilState<UdevData> {
.iter() .iter()
.flat_map(|crtc| surfaces.get(&crtc).map(|surface| (crtc, surface))); .flat_map(|crtc| surfaces.get(&crtc).map(|surface| (crtc, surface)));
let to_render_iter: &mut dyn Iterator<Item = (&crtc::Handle, &Rc<RefCell<RenderSurface>>)> = let to_render_iter: &mut dyn Iterator<Item = (&crtc::Handle, &Rc<RefCell<SurfaceData>>)> =
if crtc.is_some() { if crtc.is_some() {
&mut option_iter &mut option_iter
} else { } else {
@ -632,6 +659,8 @@ impl AnvilState<UdevData> {
&*self.output_map.borrow(), &*self.output_map.borrow(),
self.pointer_location, self.pointer_location,
&pointer_image, &pointer_image,
#[cfg(feature = "debug")]
&device_backend.fps_texture,
&*self.dnd_icon.lock().unwrap(), &*self.dnd_icon.lock().unwrap(),
&mut *self.cursor_status.lock().unwrap(), &mut *self.cursor_status.lock().unwrap(),
&self.log, &self.log,
@ -671,7 +700,7 @@ impl AnvilState<UdevData> {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn render_surface( fn render_surface(
surface: &mut RenderSurface, surface: &mut SurfaceData,
renderer: &mut Gles2Renderer, renderer: &mut Gles2Renderer,
device_id: dev_t, device_id: dev_t,
crtc: crtc::Handle, crtc: crtc::Handle,
@ -679,11 +708,12 @@ fn render_surface(
output_map: &crate::output_map::OutputMap, output_map: &crate::output_map::OutputMap,
pointer_location: Point<f64, Logical>, pointer_location: Point<f64, Logical>,
pointer_image: &Gles2Texture, pointer_image: &Gles2Texture,
#[cfg(feature = "debug")] fps_texture: &Gles2Texture,
dnd_icon: &Option<wl_surface::WlSurface>, dnd_icon: &Option<wl_surface::WlSurface>,
cursor_status: &mut CursorImageStatus, cursor_status: &mut CursorImageStatus,
logger: &slog::Logger, logger: &slog::Logger,
) -> Result<(), SwapBuffersError> { ) -> Result<(), SwapBuffersError> {
surface.frame_submitted()?; surface.surface.frame_submitted()?;
let output = output_map let output = output_map
.find(|o| o.userdata().get::<UdevOutputId>() == Some(&UdevOutputId { device_id, crtc })) .find(|o| o.userdata().get::<UdevOutputId>() == Some(&UdevOutputId { device_id, crtc }))
@ -696,7 +726,7 @@ fn render_surface(
return Ok(()); return Ok(());
}; };
let dmabuf = surface.next_buffer()?; let dmabuf = surface.surface.next_buffer()?;
renderer.bind(dmabuf)?; renderer.bind(dmabuf)?;
// and draw to our buffer // and draw to our buffer
match renderer 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(()) Ok(())
}, },
) )
@ -770,13 +812,16 @@ fn render_surface(
.and_then(|x| x) .and_then(|x| x)
.map_err(Into::<SwapBuffersError>::into) .map_err(Into::<SwapBuffersError>::into)
{ {
Ok(()) => surface.queue_buffer().map_err(Into::<SwapBuffersError>::into), Ok(()) => surface
.surface
.queue_buffer()
.map_err(Into::<SwapBuffersError>::into),
Err(err) => Err(err), Err(err) => Err(err),
} }
} }
fn schedule_initial_render<Data: 'static>( fn schedule_initial_render<Data: 'static>(
surface: Rc<RefCell<RenderSurface>>, surface: Rc<RefCell<SurfaceData>>,
renderer: Rc<RefCell<Gles2Renderer>>, renderer: Rc<RefCell<Gles2Renderer>>,
evt_handle: &LoopHandle<'static, Data>, evt_handle: &LoopHandle<'static, Data>,
logger: ::slog::Logger, logger: ::slog::Logger,
@ -784,7 +829,7 @@ fn schedule_initial_render<Data: 'static>(
let result = { let result = {
let mut surface = surface.borrow_mut(); let mut surface = surface.borrow_mut();
let mut renderer = renderer.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 { if let Err(err) = result {
match err { match err {
@ -815,32 +860,3 @@ fn initial_render(surface: &mut RenderSurface, renderer: &mut Gles2Renderer) ->
surface.queue_buffer()?; surface.queue_buffer()?;
Ok(()) Ok(())
} }
fn import_bitmap<C: std::ops::Deref<Target = [u8]>>(
renderer: &mut Gles2Renderer,
image: &ImageBuffer<Rgba<u8>, C>,
) -> Result<Gles2Texture, Gles2Error> {
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())
})
}

View File

@ -1,5 +1,7 @@
use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration}; use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration};
#[cfg(feature = "debug")]
use smithay::backend::renderer::gles2::Gles2Texture;
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
use smithay::{ use smithay::{
backend::renderer::{ImportDma, ImportEgl}, backend::renderer::{ImportDma, ImportEgl},
@ -24,7 +26,12 @@ use crate::state::{AnvilState, Backend};
pub const OUTPUT_NAME: &str = "winit"; 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 { impl Backend for WinitData {
fn seat_name(&self) -> String { fn seat_name(&self) -> String {
@ -71,7 +78,20 @@ pub fn run_winit(
* Initialize the globals * 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 { let mode = Mode {
size, 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(()) Ok(())
}) })
.map_err(Into::<SwapBuffersError>::into) .map_err(Into::<SwapBuffersError>::into)
@ -206,6 +238,9 @@ pub fn run_winit(
state.window_map.borrow_mut().refresh(); state.window_map.borrow_mut().refresh();
state.output_map.borrow_mut().refresh(); state.output_map.borrow_mut().refresh();
} }
#[cfg(feature = "debug")]
state.backend_data.fps.tick();
} }
// Cleanup stuff // Cleanup stuff