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"
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"]

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};
#[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<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,
};
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<SessionFd>;
struct SurfaceData {
surface: RenderSurface,
#[cfg(feature = "debug")]
fps: fps_ticker::Fps,
}
struct BackendData {
_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)>,
#[cfg(feature = "debug")]
fps_texture: Gles2Texture,
renderer: Rc<RefCell<Gles2Renderer>>,
gbm: GbmDevice<SessionFd>,
registration_token: RegistrationToken,
@ -272,7 +280,7 @@ fn scan_connectors(
output_map: &mut crate::output_map::OutputMap,
signaler: &Signaler<SessionSignal>,
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):
let res_handles = device.resource_handles().unwrap();
@ -321,7 +329,7 @@ fn scan_connectors(
let renderer_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())
{
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<UdevData> {
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<UdevData> {
renderer,
gbm,
pointer_images: Vec::new(),
#[cfg(feature = "debug")]
fps_texture,
dev_id,
},
);
@ -596,7 +623,7 @@ impl AnvilState<UdevData> {
.iter()
.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() {
&mut option_iter
} else {
@ -632,6 +659,8 @@ impl AnvilState<UdevData> {
&*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<UdevData> {
#[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<f64, Logical>,
pointer_image: &Gles2Texture,
#[cfg(feature = "debug")] fps_texture: &Gles2Texture,
dnd_icon: &Option<wl_surface::WlSurface>,
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::<UdevOutputId>() == 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::<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),
}
}
fn schedule_initial_render<Data: 'static>(
surface: Rc<RefCell<RenderSurface>>,
surface: Rc<RefCell<SurfaceData>>,
renderer: Rc<RefCell<Gles2Renderer>>,
evt_handle: &LoopHandle<'static, Data>,
logger: ::slog::Logger,
@ -784,7 +829,7 @@ fn schedule_initial_render<Data: 'static>(
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<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};
#[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::<SwapBuffersError>::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