diff --git a/Cargo.lock b/Cargo.lock index 3336870d..0e65aefa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,10 +35,11 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.7.6" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" dependencies = [ + "cfg-if", "getrandom", "once_cell", "version_check", @@ -156,18 +157,18 @@ checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" [[package]] name = "bytemuck" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aec14f5d4e6e3f927cd0c81f72e5710d95ee9019fbeb4b3021193867491bfd8" +checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9e1f5fa78f69496407a27ae9ed989e3c3b072310286f5ef385525e4cbc24a9" +checksum = "5fe233b960f12f8007e3db2d136e3cb1c291bfd7396e384ee76025fc1a3932b4" dependencies = [ "proc-macro2", "quote", @@ -176,9 +177,9 @@ dependencies = [ [[package]] name = "calloop" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a22a6a8f622f797120d452c630b0ab12e1331a1a753e2039ce7868d4ac77b4ee" +checksum = "595eb0438b3c6d262395fe30e6de9a61beb57ea56290b00a07f227fe6e20cbf2" dependencies = [ "log", "nix 0.24.2", @@ -189,9 +190,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.74" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" +checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f" [[package]] name = "cfg-if" @@ -535,9 +536,9 @@ checksum = "2ab5fa33485cd85ac354df485819a63360fefa312fe04cffe65e6f175be1522c" [[package]] name = "egui" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb095a8b9feb9b7ff8f00b6776dffcef059538a3f4a91238e03c900e9c9ad9a2" +checksum = "fc9fcd393c3daaaf5909008a1d948319d538b79c51871e4df0993260260a94e4" dependencies = [ "ahash", "epaint", @@ -545,20 +546,39 @@ dependencies = [ ] [[package]] -name = "emath" -version = "0.18.0" +name = "egui_glow" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c223f58c7e38abe1770f367b969f1b3fbd4704b67666bcb65dbb1adb0980ba72" +checksum = "ad77d4a00402bae9658ee64be148f4b2a0b38e4fc7874970575ca01ed1c5b75d" +dependencies = [ + "bytemuck", + "egui", + "glow", + "memoffset", + "tracing", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "emath" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9542a40106fdba943a055f418d1746a050e1a903a049b030c2b097d4686a33cf" +dependencies = [ + "bytemuck", +] [[package]] name = "epaint" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c29567088888e8ac3e8f61bbb2ddc820207ebb8d69eefde5bcefa06d65e4e89" +checksum = "5ba04741be7f6602b1a1b28f1082cce45948a7032961c52814f8946b28493300" dependencies = [ "ab_glyph", "ahash", "atomic_refcell", + "bytemuck", "emath", "nohash-hasher", "parking_lot", @@ -695,6 +715,18 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "glow" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8bd5877156a19b8ac83a29b2306fe20537429d318f3ff0a1a2119f8d9c61919" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -828,9 +860,9 @@ checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" [[package]] name = "libloading" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ "cfg-if", "winapi", @@ -922,9 +954,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498" +checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" dependencies = [ "libc", ] @@ -1151,9 +1183,9 @@ checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" [[package]] name = "owned_ttf_parser" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4665508572151759e8d60404e20dc096ef93a99801a05ac2ac6e43bf5b4ca187" +checksum = "18904d3c65493a9f0d7542293d1a7f69bfdc309a6b9ef4f46dc3e58b0577edc5" dependencies = [ "ttf-parser", ] @@ -1187,6 +1219,12 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + [[package]] name = "pkg-config" version = "0.3.26" @@ -1207,9 +1245,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro-crate" @@ -1319,9 +1357,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" dependencies = [ "aho-corasick", "memchr", @@ -1330,9 +1368,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "remove_dir_all" @@ -1512,7 +1550,7 @@ checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "smithay" version = "0.3.0" -source = "git+https://github.com/Smithay//smithay?rev=b0ec5090df#b0ec5090df54e711a59b67869b495795053574e5" +source = "git+https://github.com/pop-os/smithay?rev=c8aaa059e8#c8aaa059e841886ed9e689e0cc0d0cb821b97b71" dependencies = [ "appendlist", "bitflags", @@ -1525,6 +1563,7 @@ dependencies = [ "drm-fourcc", "gbm", "gl_generator", + "glow", "indexmap", "input", "io-lifetimes", @@ -1576,13 +1615,12 @@ dependencies = [ [[package]] name = "smithay-egui" version = "0.1.0" -source = "git+https://github.com/Smithay/smithay-egui.git?rev=939febaf#939febafd4df990b412213abff992ab4a4b9bd44" +source = "git+https://github.com/Smithay/smithay-egui.git?rev=7334d0c53#7334d0c533ad307b3359cd4931bfb1ad4c34b178" dependencies = [ "cgmath", "egui", - "lazy_static", + "egui_glow", "memoffset", - "slog", "smithay", "xkbcommon 0.4.1", ] @@ -1678,9 +1716,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fab5c8b9980850e06d92ddbe3ab839c062c801f3927c0fb8abd6fc8e918fbca" +checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ "itoa", "libc", @@ -1698,9 +1736,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" [[package]] name = "time-macros" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bb801831d812c562ae7d2bfb531f26e66e4e1f6b17307ba4149c5064710e5b" +checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" dependencies = [ "time-core", ] @@ -1714,6 +1752,26 @@ dependencies = [ "serde", ] +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + [[package]] name = "ttf-parser" version = "0.17.1" diff --git a/Cargo.toml b/Cargo.toml index 1dbebd11..954ec444 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ slog-stdlog = "4.1" serde = { version = "1", features = ["derive"] } serde_json = "1" sendfd = "0.4.1" -egui = { version = "0.18.1", optional = true } +egui = { version = "0.19.0", optional = true } edid-rs = { version = "0.1" } png = "0.17.5" lazy_static = "1.4.0" @@ -36,19 +36,18 @@ cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", branch [dependencies.smithay] version = "0.3" git = "https://github.com/Smithay/smithay.git" -rev = "606d2d5c" +rev = "b297c93edc" default-features = false -features = ["backend_drm", "backend_gbm", "backend_egl", "backend_libinput", "backend_session_libseat", "backend_udev", "backend_winit", "backend_x11", "desktop", "use_system_lib", "renderer_gl", "renderer_multi", "wayland_frontend", "slog-stdlog"] +features = ["backend_drm", "backend_gbm", "backend_egl", "backend_libinput", "backend_session_libseat", "backend_udev", "backend_winit", "backend_x11", "desktop", "use_system_lib", "renderer_glow", "renderer_multi", "wayland_frontend", "slog-stdlog"] [dependencies.smithay-egui] git = "https://github.com/Smithay/smithay-egui.git" -rev = "939febaf" +rev = "7334d0c53" optional = true [features] -default = [] +default = ["debug"] debug = ["egui", "smithay-egui"] -experimental = [] [profile.dev] lto = "thin" @@ -61,4 +60,4 @@ debug = true lto = "fat" [patch."https://github.com/Smithay/smithay.git"] -smithay = { git = "https://github.com/Smithay//smithay", rev = "b0ec5090df" } +smithay = { git = "https://github.com/pop-os/smithay", rev = "c8aaa059e8" } diff --git a/src/backend/kms/mod.rs b/src/backend/kms/mod.rs index 2637313d..c64f984f 100644 --- a/src/backend/kms/mod.rs +++ b/src/backend/kms/mod.rs @@ -19,19 +19,20 @@ use anyhow::{Context, Result}; use smithay::{ backend::{ allocator::{dmabuf::Dmabuf, gbm::GbmDevice, Format}, - drm::{DrmDevice, DrmEvent, DrmNode, GbmBufferedSurface, NodeType}, + drm::{DrmDevice, DrmEvent, DrmEventTime, DrmNode, GbmBufferedSurface, NodeType}, egl::{EGLContext, EGLDevice, EGLDisplay}, input::InputEvent, libinput::{LibinputInputBackend, LibinputSessionInterface}, renderer::{ damage::DamageTrackedRenderer, - gles2::{Gles2Renderbuffer, Gles2Renderer}, + gles2::Gles2Renderbuffer, + glow::GlowRenderer, multigpu::{egl::EglGlesBackend, GpuManager}, - Bind, }, session::{auto::AutoSession, Session, Signal}, udev::{all_gpus, primary_gpu, UdevBackend, UdevEvent}, }, + desktop::utils::OutputPresentationFeedback, output::{Mode as OutputMode, Output, PhysicalProperties, Subpixel}, reexports::{ calloop::{ @@ -41,6 +42,7 @@ use smithay::{ drm::control::{connector, crtc, Device as ControlDevice, ModeTypeFlags}, input::Libinput, nix::{fcntl::OFlag, sys::stat::dev_t}, + wayland_protocols::wp::presentation_time::server::wp_presentation_feedback, wayland_server::{protocol::wl_surface::WlSurface, DisplayHandle, Resource}, }, utils::{ @@ -69,7 +71,7 @@ use super::render::{CursorMode, GlMultiRenderer}; pub struct KmsState { devices: HashMap, - pub api: GpuManager>, + pub api: GpuManager>, pub primary: DrmNode, session: AutoSession, signaler: Signaler, @@ -89,7 +91,13 @@ pub struct Device { } pub struct Surface { - surface: Option>>, SessionFd>>, + surface: Option< + GbmBufferedSurface< + Rc>>, + SessionFd, + Option, + >, + >, damage_tracker: DamageTrackedRenderer, connector: connector::Handle, output: Output, @@ -131,6 +139,7 @@ pub fn init_backend( &data.state.common.event_loop_handle, output, None, + None, ) { slog_scope::crit!( "Error scheduling event loop for output {}: {:?}", @@ -148,7 +157,7 @@ pub fn init_backend( .map_err(|err| err.error) .context("Failed to initialize session event source")?; - let api = GpuManager::new(EglGlesBackend::::default(), None) + let api = GpuManager::new(EglGlesBackend::::default(), None) .context("Failed to initialize renderers")?; // TODO get this info from system76-power, if available and setup a watcher @@ -267,6 +276,7 @@ pub fn init_backend( if let Err(err) = data.state.backend.kms().schedule_render( &data.state.common.event_loop_handle, output, + None, if !sessions.is_empty() { Some(sessions) } else { @@ -372,8 +382,44 @@ impl State { if let Some(device) = data.state.backend.kms().devices.get_mut(&drm_node) { if let Some(surface) = device.surfaces.get_mut(&crtc) { match surface.surface.as_mut().map(|x| x.frame_submitted()) { - Some(Ok(_)) => { - let _submit_time = metadata.take().map(|data| data.time); + Some(Ok(feedback)) => { + if let Some(mut feedback) = feedback.flatten() { + let submit_time = + match metadata.take().map(|data| data.time) { + Some(DrmEventTime::Monotonic(tp)) => Some(tp), + _ => None, + }; + let seq = metadata + .as_ref() + .map(|metadata| metadata.sequence) + .unwrap_or(0); + + let (clock, flags) = if let Some(tp) = submit_time { + ( + tp.into(), + wp_presentation_feedback::Kind::Vsync + | wp_presentation_feedback::Kind::HwClock + | wp_presentation_feedback::Kind::HwCompletion, + ) + } else { + ( + data.state.common.clock.now(), + wp_presentation_feedback::Kind::Vsync, + ) + }; + + feedback.presented( + clock, + surface + .output + .current_mode() + .map(|mode| mode.refresh as u32) + .unwrap_or_default(), + seq as u64, + flags, + ); + } + surface.pending = false; surface.dirty.then(|| surface.output.clone()) } @@ -400,9 +446,19 @@ impl State { .extend(sessions.borrow_mut().drain(..)); } + let output_refresh = match output.current_mode() { + Some(mode) => mode.refresh, + None => return, + }; + // TODO: Record rendering times and use sliding window to estimate duration for next render + let repaint_delay = Duration::from_secs_f64( + ((1000.0 / output_refresh as f64) * 0.6).max(0.003), // for now we assume we need at least 3ms + ); + if let Err(err) = data.state.backend.kms().schedule_render( &data.state.common.event_loop_handle, &output, + Some(repaint_delay), scheduled_sessions, ) { slog_scope::warn!("Failed to schedule render: {}", err); @@ -738,7 +794,7 @@ impl Surface { pub fn render_output( &mut self, dh: &DisplayHandle, - api: &mut GpuManager>, + api: &mut GpuManager>, target_node: &DrmNode, state: &mut Common, screencopy: Option<&[(ScreencopySession, BufferParams)]>, @@ -755,13 +811,10 @@ impl Surface { .next_buffer() .with_context(|| "Failed to allocate buffer")?; - renderer - .bind(buffer.clone()) - .with_context(|| "Failed to bind buffer")?; - - match render::render_output::<_, Gles2Renderbuffer, _>( + match render::render_output::( Some(&render_node), &mut renderer, + buffer.clone(), &mut self.damage_tracker, age as usize, state, @@ -771,10 +824,15 @@ impl Surface { #[cfg(feature = "debug")] Some(&mut self.fps), ) { - Ok((_damage, states)) => { + Ok((damage, states)) => { + let feedback = if damage.is_some() { + Some(state.take_presentation_feedback(&self.output, &states)) + } else { + None + }; state.send_frames(&self.output, &states); surface - .queue_buffer() + .queue_buffer(feedback) .with_context(|| "Failed to submit buffer for display")?; } Err(err) => { @@ -895,6 +953,7 @@ impl KmsState { if let Err(err) = self.schedule_render( loop_handle, output, + None, if !sessions.is_empty() { Some(sessions) } else { @@ -963,6 +1022,7 @@ impl KmsState { &mut self, loop_handle: &LoopHandle<'_, Data>, output: &Output, + delay: Option, mut screencopy_sessions: Option>, ) -> Result<(), InsertError> { if let Some((device, crtc, surface)) = self @@ -977,19 +1037,6 @@ impl KmsState { if !surface.pending { surface.dirty = false; surface.pending = true; - /* - let instant = surface - .last_submit - .as_ref() - .and_then(|x| match x { - DrmEventTime::Monotonic(instant) => Some(instant), - DrmEventTime::Realtime(_) => None, - }) - .map(|i| { - *i + Duration::from_secs_f64(1.0 / surface.refresh_rate as f64) - - Duration::from_millis(20) // render budget - }); - */ let device = *device; let crtc = *crtc; @@ -997,10 +1044,11 @@ impl KmsState { loop_handle.remove(token); } surface.render_timer_token = Some(loop_handle.insert_source( - //if surface.vrr || instant.is_none() { - Timer::immediate(), /*} else { - Timer::from_deadline(instant.unwrap()) - }*/ + if surface.vrr || delay.is_none() { + Timer::immediate() + } else { + Timer::from_duration(delay.unwrap()) + }, move |_time, _, data| { let backend = data.state.backend.kms(); if let Some(device) = backend.devices.get_mut(&device) { @@ -1020,7 +1068,7 @@ impl KmsState { if backend.session.is_active() { slog_scope::error!("Error rendering: {}", err); return TimeoutAction::ToDuration(Duration::from_secs_f64( - 1.0 / surface.refresh_rate as f64, + (1000.0 / surface.refresh_rate as f64) - 0.003, )); } } diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index dd2fa461..9c68a62d 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -15,7 +15,7 @@ use smithay::{ }, reexports::wayland_server::protocol::wl_surface, render_elements, - utils::{IsAlive, Logical, Point, Scale, Transform}, + utils::{IsAlive, Logical, Monotonic, Point, Scale, Time, Transform}, wayland::compositor::{get_role, with_states}, }; use std::{ @@ -24,6 +24,7 @@ use std::{ collections::HashMap, io::Read, sync::Mutex, + time::Duration, }; use xcursor::{ parser::{parse_xcursor, Image}, @@ -193,93 +194,85 @@ pub fn draw_cursor( seat: &Seat, location: Point, scale: Scale, - start_time: &std::time::Instant, + time: Time, draw_default: bool, ) -> Vec> where - R: Renderer + ImportAll + ImportMem, + R: Renderer + ImportMem + ImportAll, ::TextureId: Clone + 'static, { // draw the cursor as relevant - { - // reset the cursor if the surface is no longer alive - let cursor_status = seat - .user_data() - .get::>() - .map(|cell| { - let mut cursor_status = cell.borrow_mut(); - if let CursorImageStatus::Surface(ref surface) = *cursor_status { - if !surface.alive() { - *cursor_status = CursorImageStatus::Default; - } + // reset the cursor if the surface is no longer alive + let cursor_status = seat + .user_data() + .get::>() + .map(|cell| { + let mut cursor_status = cell.borrow_mut(); + if let CursorImageStatus::Surface(ref surface) = *cursor_status { + if !surface.alive() { + *cursor_status = CursorImageStatus::Default; } - cursor_status.clone() - }) - .unwrap_or(CursorImageStatus::Default); + } + cursor_status.clone() + }) + .unwrap_or(CursorImageStatus::Default); - if let CursorImageStatus::Surface(ref wl_surface) = cursor_status { - return draw_surface_cursor(wl_surface, location.to_i32_round(), scale); - } else if draw_default && CursorImageStatus::Default == cursor_status { - let integer_scale = scale.x.max(scale.y).ceil() as u32; + if let CursorImageStatus::Surface(ref wl_surface) = cursor_status { + return draw_surface_cursor(wl_surface, location.to_i32_round(), scale); + } else if draw_default && CursorImageStatus::Default == cursor_status { + let integer_scale = scale.x.max(scale.y).ceil() as u32; - let seat_userdata = seat.user_data(); - seat_userdata.insert_if_missing(CursorState::default); - let state = seat_userdata.get::().unwrap(); - let frame = state - .cursor - .get_image(integer_scale, start_time.elapsed().as_millis() as u32); + let seat_userdata = seat.user_data(); + seat_userdata.insert_if_missing(CursorState::default); + let state = seat_userdata.get::().unwrap(); + let frame = state.cursor.get_image( + integer_scale, + Into::::into(time).as_millis() as u32, + ); - let mut cache = state.image_cache.borrow_mut(); - let pointer_images = cache - .entry(( - TypeId::of::::TextureId>>(), - renderer.id(), - )) - .or_default(); + let mut cache = state.image_cache.borrow_mut(); + let pointer_images = cache + .entry((TypeId::of::>(), renderer.id())) + .or_default(); - let maybe_image = pointer_images - .iter() - .find_map(|(image, texture)| if image == &frame { Some(texture) } else { None }) - .and_then(|texture| { - texture.downcast_ref::::TextureId>>() - }); - let pointer_image = match maybe_image { - Some(image) => image, - None => { - let texture = TextureBuffer::from_memory( - renderer, - &frame.pixels_rgba, - (frame.width as i32, frame.height as i32), - false, - integer_scale as i32, - Transform::Normal, - None, - ) - .expect("Failed to import cursor bitmap"); - pointer_images.push((frame.clone(), Box::new(texture.clone()))); - pointer_images - .last() - .and_then(|(_, i)| { - i.downcast_ref::::TextureId>>() - }) - .unwrap() - } - }; - - let hotspot = - Point::::from((frame.xhot as i32, frame.yhot as i32)).to_f64(); - *state.current_image.borrow_mut() = Some(frame); - - return vec![CursorRenderElement::Static( - TextureRenderElement::from_texture_buffer( - (location - hotspot).to_physical(scale), - pointer_image, + let maybe_image = pointer_images + .iter() + .find_map(|(image, texture)| if image == &frame { Some(texture) } else { None }) + .and_then(|texture| texture.downcast_ref::>()); + let pointer_image = match maybe_image { + Some(image) => image, + None => { + let texture = TextureBuffer::from_memory( + renderer, + &frame.pixels_rgba, + (frame.width as i32, frame.height as i32), + false, + integer_scale as i32, + Transform::Normal, None, - None, - ), - )]; - } else { - Vec::new() - } + ) + .expect("Failed to import cursor bitmap"); + pointer_images.push((frame.clone(), Box::new(texture.clone()))); + pointer_images + .last() + .and_then(|(_, i)| i.downcast_ref::>()) + .unwrap() + } + }; + + let hotspot = Point::::from((frame.xhot as i32, frame.yhot as i32)).to_f64(); + *state.current_image.borrow_mut() = Some(frame); + + return vec![CursorRenderElement::Static( + TextureRenderElement::from_texture_buffer( + (location - hotspot).to_physical(scale), + pointer_image, + None, + None, + None, + ), + )]; + } else { + Vec::new() } } diff --git a/src/backend/render/element.rs b/src/backend/render/element.rs new file mode 100644 index 00000000..5513ec40 --- /dev/null +++ b/src/backend/render/element.rs @@ -0,0 +1,302 @@ +use crate::shell::{CosmicMappedRenderElement, WorkspaceRenderElement}; + +use smithay::{ + backend::renderer::{ + element::{texture::TextureRenderElement, Element, RenderElement, UnderlyingStorage}, + gles2::{Gles2Frame, Gles2Texture}, + glow::GlowRenderer, + multigpu::Error as MultiError, + Frame, ImportAll, Renderer, + }, + utils::{Physical, Point, Rectangle, Scale}, +}; + +use super::{cursor::CursorRenderElement, GlMultiFrame, GlMultiRenderer}; + +pub enum CosmicElement +where + R: AsGlowRenderer + Renderer + ImportAll, + ::TextureId: 'static, + ::Frame: AsGles2Frame, +{ + Workspace(WorkspaceRenderElement), + Cursor(CursorRenderElement), + MoveGrab(CosmicMappedRenderElement), + #[cfg(feature = "debug")] + Egui(TextureRenderElement), +} + +impl Element for CosmicElement +where + R: AsGlowRenderer + Renderer + ImportAll, + ::TextureId: 'static, + ::Frame: AsGles2Frame, +{ + fn id(&self) -> &smithay::backend::renderer::element::Id { + match self { + CosmicElement::Workspace(elem) => elem.id(), + CosmicElement::Cursor(elem) => elem.id(), + CosmicElement::MoveGrab(elem) => elem.id(), + #[cfg(feature = "debug")] + CosmicElement::Egui(elem) => elem.id(), + } + } + + fn current_commit(&self) -> smithay::backend::renderer::utils::CommitCounter { + match self { + CosmicElement::Workspace(elem) => elem.current_commit(), + CosmicElement::Cursor(elem) => elem.current_commit(), + CosmicElement::MoveGrab(elem) => elem.current_commit(), + #[cfg(feature = "debug")] + CosmicElement::Egui(elem) => elem.current_commit(), + } + } + + fn src(&self) -> Rectangle { + match self { + CosmicElement::Workspace(elem) => elem.src(), + CosmicElement::Cursor(elem) => elem.src(), + CosmicElement::MoveGrab(elem) => elem.src(), + #[cfg(feature = "debug")] + CosmicElement::Egui(elem) => elem.src(), + } + } + + fn geometry(&self, scale: Scale) -> Rectangle { + match self { + CosmicElement::Workspace(elem) => elem.geometry(scale), + CosmicElement::Cursor(elem) => elem.geometry(scale), + CosmicElement::MoveGrab(elem) => elem.geometry(scale), + #[cfg(feature = "debug")] + CosmicElement::Egui(elem) => elem.geometry(scale), + } + } + + fn location(&self, scale: Scale) -> Point { + match self { + CosmicElement::Workspace(elem) => elem.location(scale), + CosmicElement::Cursor(elem) => elem.location(scale), + CosmicElement::MoveGrab(elem) => elem.location(scale), + #[cfg(feature = "debug")] + CosmicElement::Egui(elem) => elem.location(scale), + } + } + + fn transform(&self) -> smithay::utils::Transform { + match self { + CosmicElement::Workspace(elem) => elem.transform(), + CosmicElement::Cursor(elem) => elem.transform(), + CosmicElement::MoveGrab(elem) => elem.transform(), + #[cfg(feature = "debug")] + CosmicElement::Egui(elem) => elem.transform(), + } + } + + fn damage_since( + &self, + scale: Scale, + commit: Option, + ) -> Vec> { + match self { + CosmicElement::Workspace(elem) => elem.damage_since(scale, commit), + CosmicElement::Cursor(elem) => elem.damage_since(scale, commit), + CosmicElement::MoveGrab(elem) => elem.damage_since(scale, commit), + #[cfg(feature = "debug")] + CosmicElement::Egui(elem) => elem.damage_since(scale, commit), + } + } + + fn opaque_regions(&self, scale: Scale) -> Vec> { + match self { + CosmicElement::Workspace(elem) => elem.opaque_regions(scale), + CosmicElement::Cursor(elem) => elem.opaque_regions(scale), + CosmicElement::MoveGrab(elem) => elem.opaque_regions(scale), + #[cfg(feature = "debug")] + CosmicElement::Egui(elem) => elem.opaque_regions(scale), + } + } +} + +impl RenderElement for CosmicElement { + fn draw( + &self, + renderer: &mut GlowRenderer, + frame: &mut ::Frame, + location: Point, + scale: Scale, + damage: &[Rectangle], + log: &slog::Logger, + ) -> Result<(), ::Error> { + match self { + CosmicElement::Workspace(elem) => { + elem.draw(renderer, frame, location, scale, damage, log) + } + CosmicElement::Cursor(elem) => elem.draw(renderer, frame, location, scale, damage, log), + CosmicElement::MoveGrab(elem) => { + elem.draw(renderer, frame, location, scale, damage, log) + } + #[cfg(feature = "debug")] + CosmicElement::Egui(elem) => elem.draw(renderer, frame, location, scale, damage, log), + } + } + + fn underlying_storage( + &self, + renderer: &GlowRenderer, + ) -> Option> { + match self { + CosmicElement::Workspace(elem) => elem.underlying_storage(renderer), + CosmicElement::Cursor(elem) => elem.underlying_storage(renderer), + CosmicElement::MoveGrab(elem) => elem.underlying_storage(renderer), + #[cfg(feature = "debug")] + CosmicElement::Egui(elem) => elem.underlying_storage(renderer), + } + } +} + +impl<'a> RenderElement> for CosmicElement> { + fn draw( + &self, + renderer: &mut GlMultiRenderer<'a>, + frame: &mut as Renderer>::Frame, + location: Point, + scale: Scale, + damage: &[Rectangle], + log: &slog::Logger, + ) -> Result<(), as Renderer>::Error> { + match self { + CosmicElement::Workspace(elem) => { + elem.draw(renderer, frame, location, scale, damage, log) + } + CosmicElement::Cursor(elem) => elem.draw(renderer, frame, location, scale, damage, log), + CosmicElement::MoveGrab(elem) => { + elem.draw(renderer, frame, location, scale, damage, log) + } + #[cfg(feature = "debug")] + CosmicElement::Egui(elem) => { + let glow_renderer = renderer.glow_renderer_mut(); + let gles2_frame = frame.gles2_frame_mut(); + elem.draw(glow_renderer, gles2_frame, location, scale, damage, log) + .map_err(|err| MultiError::Render(err)) + } + } + } + + fn underlying_storage( + &self, + renderer: &GlMultiRenderer<'a>, + ) -> Option>> { + match self { + CosmicElement::Workspace(elem) => elem.underlying_storage(renderer), + CosmicElement::Cursor(elem) => elem.underlying_storage(renderer), + CosmicElement::MoveGrab(elem) => elem.underlying_storage(renderer), + #[cfg(feature = "debug")] + CosmicElement::Egui(elem) => { + let glow_renderer = renderer.glow_renderer(); + match elem.underlying_storage(glow_renderer) { + Some(UnderlyingStorage::Wayland(buffer)) => { + Some(UnderlyingStorage::Wayland(buffer)) + } + _ => None, + } + } + } + } +} + +impl From> for CosmicElement +where + R: Renderer + ImportAll + AsGlowRenderer, + ::TextureId: 'static, + ::Frame: AsGles2Frame, +{ + fn from(elem: WorkspaceRenderElement) -> Self { + Self::Workspace(elem) + } +} + +impl From> for CosmicElement +where + R: Renderer + ImportAll + AsGlowRenderer, + ::TextureId: 'static, + ::Frame: AsGles2Frame, +{ + fn from(elem: CursorRenderElement) -> Self { + Self::Cursor(elem) + } +} + +impl From> for CosmicElement +where + R: Renderer + ImportAll + AsGlowRenderer, + ::TextureId: 'static, + ::Frame: AsGles2Frame, +{ + fn from(elem: CosmicMappedRenderElement) -> Self { + Self::MoveGrab(elem) + } +} + +impl From> for CosmicElement +where + R: Renderer + ImportAll + AsGlowRenderer, + ::TextureId: 'static, + ::Frame: AsGles2Frame, +{ + fn from(elem: TextureRenderElement) -> Self { + Self::Egui(elem) + } +} + +pub trait AsGlowRenderer +where + Self: Renderer, + ::Frame: AsGles2Frame, +{ + fn glow_renderer(&self) -> &GlowRenderer; + fn glow_renderer_mut(&mut self) -> &mut GlowRenderer; +} + +impl AsGlowRenderer for GlowRenderer { + fn glow_renderer(&self) -> &GlowRenderer { + self + } + fn glow_renderer_mut(&mut self) -> &mut GlowRenderer { + self + } +} + +impl<'a> AsGlowRenderer for GlMultiRenderer<'a> { + fn glow_renderer(&self) -> &GlowRenderer { + self.as_ref() + } + fn glow_renderer_mut(&mut self) -> &mut GlowRenderer { + self.as_mut() + } +} + +pub trait AsGles2Frame +where + Self: Frame, +{ + fn gles2_frame(&self) -> &Gles2Frame; + fn gles2_frame_mut(&mut self) -> &mut Gles2Frame; +} + +impl AsGles2Frame for Gles2Frame { + fn gles2_frame(&self) -> &Gles2Frame { + self + } + fn gles2_frame_mut(&mut self) -> &mut Gles2Frame { + self + } +} + +impl AsGles2Frame for GlMultiFrame { + fn gles2_frame(&self) -> &Gles2Frame { + self.as_ref() + } + fn gles2_frame_mut(&mut self) -> &mut Gles2Frame { + self.as_mut() + } +} diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index 96de627a..02cd7fd8 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -1,18 +1,12 @@ // SPDX-License-Identifier: GPL-3.0-only #[cfg(feature = "debug")] +use crate::{debug::fps_ui, state::Fps, utils::prelude::*}; use crate::{ - debug::{debug_ui, fps_ui, log_ui, EguiFrame}, - state::Fps, - utils::prelude::*, -}; -use crate::{ - shell::{ - layout::floating::SeatMoveGrabState, CosmicMappedRenderElement, WorkspaceRenderElement, - }, + shell::{layout::floating::SeatMoveGrabState, CosmicMappedRenderElement}, state::Common, wayland::{ - handlers::{data_device::get_dnd_icon, screencopy::render_to_buffer}, + handlers::{data_device::get_dnd_icon, screencopy::render_session}, protocols::{ screencopy::{ BufferParams, CursorMode as ScreencopyCursorMode, Session as ScreencopySession, @@ -28,42 +22,38 @@ use smithay::{ allocator::dmabuf::Dmabuf, drm::DrmNode, renderer::{ + buffer_dimensions, damage::{ DamageTrackedRenderer, DamageTrackedRendererError as RenderError, OutputNoMode, }, - element::RenderElementStates, - gles2::{Gles2Renderbuffer, Gles2Renderer}, + element::{RenderElement, RenderElementStates}, + gles2::{Gles2Error, Gles2Renderbuffer}, + glow::GlowRenderer, multigpu::{egl::EglGlesBackend, MultiFrame, MultiRenderer}, Bind, Blit, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, TextureFilter, }, }, output::Output, utils::{Physical, Rectangle}, + wayland::dmabuf::get_dmabuf, }; pub mod cursor; use self::cursor::CursorRenderElement; +pub mod element; +use self::element::{AsGles2Frame, AsGlowRenderer, CosmicElement}; pub type GlMultiRenderer<'a> = MultiRenderer< 'a, 'a, - EglGlesBackend, - EglGlesBackend, + EglGlesBackend, + EglGlesBackend, Gles2Renderbuffer, >; -pub type GlMultiFrame = MultiFrame, EglGlesBackend>; +pub type GlMultiFrame = MultiFrame, EglGlesBackend>; pub static CLEAR_COLOR: [f32; 4] = [0.153, 0.161, 0.165, 1.0]; -smithay::render_elements! { - pub CosmicElement where R: ImportAll; - WorkspaceElement=WorkspaceRenderElement, - CursorElement=CursorRenderElement, - MoveGrabRenderElement=CosmicMappedRenderElement, - //#[cfg(feature = "debug")] - //EguiFrame=EguiFrame, -} - #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum CursorMode { None, @@ -79,6 +69,7 @@ pub fn cursor_elements( ) -> Vec where R: Renderer + ImportAll + ImportMem, + ::Frame: AsGles2Frame, ::TextureId: Clone + 'static, E: From> + From>, { @@ -101,7 +92,7 @@ where seat, location, scale.into(), - &state.start_time, + state.clock.now(), mode != CursorMode::NotDefault, ) .into_iter() @@ -132,16 +123,17 @@ where elements } -pub fn render_output( +pub fn render_output( gpu: Option<&DrmNode>, renderer: &mut R, + target: Target, damage_tracker: &mut DamageTrackedRenderer, age: usize, state: &mut Common, output: &Output, cursor_mode: CursorMode, screencopy: Option<(Source, &[(ScreencopySession, BufferParams)])>, - #[cfg(feature = "debug")] mut fps: Option<&mut Fps>, + #[cfg(feature = "debug")] fps: Option<&mut Fps>, ) -> Result<(Option>>, RenderElementStates), RenderError> where R: Renderer @@ -149,16 +141,21 @@ where + ImportMem + ExportMem + Bind - + Offscreen - + Bind - + Blit, + + Bind + + Offscreen + + Blit + + AsGlowRenderer, + ::Frame: AsGles2Frame, ::TextureId: Clone + 'static, + ::Error: From, + CosmicElement: RenderElement, Source: Clone, { let handle = state.shell.workspaces.active(output).handle; render_workspace( gpu, renderer, + target, damage_tracker, age, state, @@ -166,12 +163,15 @@ where &handle, cursor_mode, screencopy, + #[cfg(feature = "debug")] + fps, ) } -pub fn render_workspace( +pub fn render_workspace( gpu: Option<&DrmNode>, renderer: &mut R, + target: Target, damage_tracker: &mut DamageTrackedRenderer, age: usize, state: &mut Common, @@ -187,11 +187,15 @@ where + ImportMem + ExportMem + Bind - + Offscreen - + Bind - + Blit, - Source: Clone, + + Bind + + Offscreen + + Blit + + AsGlowRenderer, + ::Frame: AsGles2Frame, ::TextureId: Clone + 'static, + ::Error: From, + CosmicElement: RenderElement, + Source: Clone, { #[cfg(feature = "debug")] if let Some(ref mut fps) = fps { @@ -219,68 +223,67 @@ where #[cfg(feature = "debug")] { - // TODO add debug elements - let workspace = &state.shell.spaces[space_idx]; - let output_geo = workspace - .space - .output_geometry(output) - .unwrap_or(Rectangle::from_loc_and_size((0, 0), (0, 0))); + let output_geo = output.geometry(); let scale = output.current_scale().fractional_scale(); - if let Some(fps) = fps { + if let Some(fps) = fps.as_mut() { let fps_overlay = fps_ui( - _gpu, + gpu, state, + renderer.glow_renderer_mut(), fps, - output_geo.to_f64().to_physical(scale), + Rectangle::from_loc_and_size( + (0, 0), + (output_geo.size.w.min(400), output_geo.size.h.min(800)), + ), scale, - ); - custom_elements.push(fps_overlay.into()); - } - - let area = Rectangle::::from_loc_and_size( - state - .shell - .space_relative_output_geometry((0.0f64, 0.0f64), output), - state.shell.global_space().to_f64().size, - ) - .to_physical(scale); - if let Some(log_ui) = log_ui(state, area, scale, output_geo.size.w as f32 * 0.6) { - custom_elements.push(log_ui.into()); - } - if let Some(debug_overlay) = debug_ui(state, area, scale) { - custom_elements.push(debug_overlay.into()); + ) + .map_err(::Error::from) + .map_err(RenderError::Rendering)?; + elements.push(fps_overlay.into()); } } elements.extend( workspace - .render_output(output) + .render_output::(output) .map_err(|_| OutputNoMode)? .into_iter() .map(Into::into), ); + renderer.bind(target).map_err(RenderError::Rendering)?; let res = damage_tracker.render_output(renderer, age, &elements, CLEAR_COLOR, None); #[cfg(feature = "debug")] - if let Some(ref mut fps) = fps { + if let Some(fps) = fps.as_mut() { fps.end(); } if let Some((source, buffers)) = screencopy { if res.is_ok() { for (session, params) in buffers { - match render_to_buffer( + match render_session( gpu.cloned(), renderer, &session, params, output.current_transform(), - |_node, renderer, dtr, age| { + |_node, buffer, renderer, dtr, age| { let res = dtr.damage_output(age, &elements, slog_scope::logger())?; if let (Some(ref damage), _) = &res { + if let Ok(dmabuf) = get_dmabuf(buffer) { + renderer.bind(dmabuf).map_err(RenderError::Rendering)?; + } else { + let size = buffer_dimensions(buffer).unwrap(); + let render_buffer = renderer + .create_buffer(size) + .map_err(RenderError::Rendering)?; + renderer + .bind(render_buffer) + .map_err(RenderError::Rendering)?; + } for rect in damage { renderer .blit_from(source.clone(), *rect, *rect, TextureFilter::Nearest) diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 78965c6e..1790aef1 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -11,13 +11,17 @@ use crate::{ use anyhow::{anyhow, Context, Result}; use smithay::{ backend::{ - renderer::{damage::DamageTrackedRenderer, gles2::Gles2Renderbuffer, ImportDma, ImportEgl}, + renderer::{ + damage::DamageTrackedRenderer, gles2::Gles2Renderbuffer, glow::GlowRenderer, ImportDma, + ImportEgl, + }, winit::{self, WinitEvent, WinitGraphicsBackend, WinitVirtualDevice}, }, desktop::layer_map_for_output, output::{Mode, Output, PhysicalProperties, Scale, Subpixel}, reexports::{ calloop::{ping, EventLoop}, + wayland_protocols::wp::presentation_time::server::wp_presentation_feedback, wayland_server::DisplayHandle, }, utils::Transform, @@ -31,7 +35,7 @@ use super::render::CursorMode; pub struct WinitState { // The winit backend currently has no notion of multiple windows - pub backend: WinitGraphicsBackend, + pub backend: WinitGraphicsBackend, output: Output, damage_tracker: DamageTrackedRenderer, screencopy: Vec<(ScreencopySession, BufferParams)>, @@ -47,9 +51,10 @@ impl WinitState { let age = self.backend.buffer_age().unwrap_or(0); let surface = self.backend.egl_surface(); - match render::render_output::<_, Gles2Renderbuffer, _>( + match render::render_output::<_, _, Gles2Renderbuffer, _>( None, self.backend.renderer(), + surface.clone(), &mut self.damage_tracker, age, state, @@ -69,6 +74,19 @@ impl WinitState { .submit(damage.as_deref()) .with_context(|| "Failed to submit buffer for display")?; state.send_frames(&self.output, &states); + if damage.is_some() { + let mut output_presentation_feedback = + state.take_presentation_feedback(&self.output, &states); + output_presentation_feedback.presented( + state.clock.now(), + self.output + .current_mode() + .map(|mode| mode.refresh as u32) + .unwrap_or_default(), + 0, + wp_presentation_feedback::Kind::Vsync, + ) + } } Err(err) => { for (session, params) in self.screencopy.drain(..) { @@ -227,7 +245,7 @@ pub fn init_backend( fn init_egl_client_side( dh: &DisplayHandle, state: &mut State, - renderer: &mut WinitGraphicsBackend, + renderer: &mut WinitGraphicsBackend, ) -> Result<()> { let bind_result = renderer.renderer().bind_wl_display(dh); match bind_result { diff --git a/src/backend/x11.rs b/src/backend/x11.rs index 70ebd11b..7c9e903b 100644 --- a/src/backend/x11.rs +++ b/src/backend/x11.rs @@ -15,9 +15,8 @@ use smithay::{ egl::{EGLContext, EGLDisplay}, input::{Event, InputEvent}, renderer::{ - damage::DamageTrackedRenderer, - gles2::{Gles2Renderbuffer, Gles2Renderer}, - Bind, ImportDma, ImportEgl, + damage::DamageTrackedRenderer, gles2::Gles2Renderbuffer, glow::GlowRenderer, Bind, + ImportDma, ImportEgl, }, x11::{Window, WindowBuilder, X11Backend, X11Event, X11Handle, X11Input, X11Surface}, }, @@ -26,6 +25,7 @@ use smithay::{ reexports::{ calloop::{ping, EventLoop, LoopHandle}, gbm::{Device as GbmDevice, FdWrapper}, + wayland_protocols::wp::presentation_time::server::wp_presentation_feedback, wayland_server::DisplayHandle, }, utils::Transform, @@ -41,7 +41,7 @@ use crate::state::Fps; pub struct X11State { allocator: Arc>>, _egl: EGLDisplay, - pub renderer: Gles2Renderer, + pub renderer: GlowRenderer, surfaces: Vec, handle: X11Handle, } @@ -194,22 +194,15 @@ pub struct Surface { } impl Surface { - pub fn render_output( - &mut self, - renderer: &mut Gles2Renderer, - state: &mut Common, - ) -> Result<()> { + pub fn render_output(&mut self, renderer: &mut GlowRenderer, state: &mut Common) -> Result<()> { let (buffer, age) = self .surface .buffer() .with_context(|| "Failed to allocate buffer")?; - renderer - .bind(buffer.clone()) - .with_context(|| "Failed to bind buffer")?; - - match render::render_output::<_, Gles2Renderbuffer, _>( + match render::render_output::<_, _, Gles2Renderbuffer, _>( None, renderer, + buffer.clone(), &mut self.damage_tracker, age as usize, state, @@ -223,12 +216,25 @@ impl Surface { #[cfg(feature = "debug")] Some(&mut self.fps), ) { - Ok((_damage, states)) => { + Ok((damage, states)) => { self.screencopy.clear(); self.surface .submit() .with_context(|| "Failed to submit buffer for display")?; state.send_frames(&self.output, &states); + if damage.is_some() { + let mut output_presentation_feedback = + state.take_presentation_feedback(&self.output, &states); + output_presentation_feedback.presented( + state.clock.now(), + self.output + .current_mode() + .map(|mode| mode.refresh as u32) + .unwrap_or_default(), + 0, + wp_presentation_feedback::Kind::Vsync, + ) + } } Err(err) => { for (session, params) in self.screencopy.drain(..) { @@ -265,7 +271,7 @@ pub fn init_backend( // Create the OpenGL context let context = EGLContext::new(&egl, None).with_context(|| "Failed to create EGL context")?; // Create a renderer - let mut renderer = unsafe { Gles2Renderer::new(context, None) } + let mut renderer = unsafe { GlowRenderer::new(context, None) } .with_context(|| "Failed to initialize renderer")?; init_egl_client_side(dh, state, &mut renderer)?; @@ -383,11 +389,10 @@ pub fn init_backend( Ok(()) } -fn init_egl_client_side( - dh: &DisplayHandle, - state: &mut State, - renderer: &mut Gles2Renderer, -) -> Result<()> { +fn init_egl_client_side(dh: &DisplayHandle, state: &mut State, renderer: &mut R) -> Result<()> +where + R: ImportEgl + ImportDma, +{ let bind_result = renderer.bind_wl_display(dh); match bind_result { Ok(_) => { diff --git a/src/debug.rs b/src/debug.rs index f6d03048..c8abf923 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -2,20 +2,27 @@ use crate::state::{Common, Fps}; use smithay::{ - backend::drm::DrmNode, + backend::{ + drm::DrmNode, + renderer::{ + element::texture::TextureRenderElement, + gles2::{Gles2Error, Gles2Texture}, + glow::GlowRenderer, + }, + }, desktop::layer_map_for_output, reexports::wayland_server::Resource, - utils::{IsAlive, Physical, Rectangle}, + utils::{IsAlive, Logical, Rectangle}, }; -pub use smithay_egui::EguiFrame; pub fn fps_ui( gpu: Option<&DrmNode>, state: &Common, + renderer: &mut GlowRenderer, fps: &mut Fps, - area: Rectangle, + area: Rectangle, scale: f64, -) -> EguiFrame { +) -> Result, Gles2Error> { use egui::widgets::plot::{Bar, BarChart, HLine, Legend, Plot}; let (max, min, avg, avg_fps) = ( @@ -24,25 +31,27 @@ pub fn fps_ui( fps.avg_frametime().as_secs_f64(), fps.avg_fps(), ); + let amount = dbg!(avg_fps.round() as usize * 2); let bars = fps .frames .iter() .rev() - .take(30) + .take(amount) .rev() .enumerate() .map(|(i, (_, d))| { let value = d.as_secs_f64(); let transformed = ((value - min) / (max - min) * 255.0).round() as u8; + Bar::new(i as f64, transformed as f64).fill(egui::Color32::from_rgb( transformed, 255 - transformed, 0, )) }) - .collect(); + .collect::>(); - fps.state.run( + fps.state.render( |ctx| { egui::Area::new("main") .anchor(egui::Align2::LEFT_TOP, (10.0, 10.0)) @@ -51,14 +60,14 @@ pub fn fps_ui( "cosmic-comp version {}", std::env!("CARGO_PKG_VERSION") )); - if let Some(hash) = std::option_env!("GIT_HASH").and_then(|x| x.get(0..8)) { + if let Some(hash) = std::option_env!("GIT_HASH").and_then(|x| x.get(0..10)) { ui.label(hash); } if !state.egui.active { - ui.label("Press Mod+Escape for debug menu"); + ui.label("Press Super+Escape for debug menu"); } else { - ui.set_max_width(label_res.rect.min.x + label_res.rect.width()); + ui.set_max_width(400.0); ui.separator(); if let Some(gpu) = gpu { @@ -73,31 +82,34 @@ pub fn fps_ui( Plot::new("FPS") .legend(Legend::default()) - .view_aspect(33.0) + .view_aspect(50.0) .include_x(0.0) - .include_x(30.0) + .include_x(amount as f64) .include_y(0.0) - .include_y(300.0) + .include_y(300) .show_x(false) .show(ui, |plot_ui| { plot_ui.bar_chart(fps_chart); + /* plot_ui.hline( HLine::new(avg) .highlight(true) .color(egui::Color32::LIGHT_BLUE), ); + */ }); } }); }, + renderer, area, scale, - 1.0, - &state.start_time, - fps.modifiers.clone(), + 0.8, + state.clock.now().into(), ) } +/* pub fn debug_ui( state: &mut Common, area: Rectangle, @@ -296,132 +308,4 @@ pub fn debug_ui( state.egui.modifiers.clone(), )) } - -pub fn log_ui( - state: &mut Common, - area: Rectangle, - scale: f64, - default_width: f32, -) -> Option { - if !state.egui.active { - return None; - } - - Some(state.egui.log_state.run( - |ctx| { - egui::SidePanel::right("Log") - .frame(egui::Frame { - inner_margin: egui::Vec2::new(10.0, 10.0).into(), - outer_margin: egui::Vec2::new(0.0, 0.0).into(), - rounding: 5.0.into(), - shadow: egui::epaint::Shadow { - extrusion: 0.0, - color: egui::Color32::TRANSPARENT, - }, - fill: egui::Color32::from_black_alpha(100), - stroke: egui::Stroke::none(), - }) - .default_width(default_width) - .show(ctx, |ui| { - egui::ScrollArea::vertical() - .always_show_scroll(true) - .stick_to_bottom() - .show(ui, |ui| { - for (_i, record) in state - .log - .debug_buffer - .lock() - .unwrap() - .iter() - .rev() - .enumerate() - { - let mut message = egui::text::LayoutJob::single_section( - record.level.as_short_str().to_string(), - egui::TextFormat::simple( - egui::FontId::monospace(16.0), - match record.level { - slog::Level::Critical => egui::Color32::RED, - slog::Level::Error => egui::Color32::LIGHT_RED, - slog::Level::Warning => egui::Color32::LIGHT_YELLOW, - slog::Level::Info => egui::Color32::LIGHT_BLUE, - slog::Level::Debug => egui::Color32::LIGHT_GREEN, - slog::Level::Trace => egui::Color32::GRAY, - }, - ), - ); - message.append( - &record.message, - 6.0, - egui::TextFormat::simple( - egui::FontId::default(), - egui::Color32::WHITE, - ), - ); - ui.vertical(|ui| { - ui.add(egui::Label::new(message)); - ui.add_space(4.0); - for (k, v) in &record.kv { - ui.horizontal(|ui| { - ui.add( - egui::Label::new(egui::RichText::new(k).code()) - .sense(egui::Sense::click()), - ) - .on_hover_cursor(egui::CursorIcon::PointingHand); - render_value(ui, v); - }); - } - }); - } - }) - }); - }, - area, - scale, - state.egui.alpha, - &state.start_time, - state.egui.modifiers.clone(), - )) -} - -fn render_value(ui: &mut egui::Ui, value: &serde_json::Value) { - use serde_json::Value::*; - - match value { - Null => { - ui.label(egui::RichText::new("null").code()); - } - Bool(val) => { - ui.label(egui::RichText::new(format!("{}", val)).code()); - } - Number(val) => { - ui.label(egui::RichText::new(format!("{}", val)).code()); - } - String(val) => { - ui.label(val); - } - Array(list) => { - ui.vertical(|ui| { - ui.label("["); - for val in list { - ui.horizontal(|ui| { - ui.add_space(4.0); - render_value(ui, val); - }); - } - ui.label("]"); - }); - } - Object(map) => { - ui.vertical(|ui| { - for (k, val) in map { - ui.horizontal(|ui| { - ui.add_space(4.0); - ui.add(egui::Label::new(egui::RichText::new(k).code())); - render_value(ui, val); - }); - } - }); - } - }; -} +*/ diff --git a/src/input/mod.rs b/src/input/mod.rs index 4c5e0dd3..03f58d64 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -17,8 +17,8 @@ use crate::{ use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType; use smithay::{ backend::input::{ - AbsolutePositionEvent, Axis, AxisSource, Device, DeviceCapability, InputBackend, - InputEvent, KeyState, PointerAxisEvent, + Axis, AxisSource, Device, DeviceCapability, InputBackend, InputEvent, KeyState, + PointerAxisEvent, }, desktop::{layer_map_for_output, WindowSurfaceType}, input::{ @@ -166,7 +166,6 @@ impl State { #[cfg(feature = "debug")] { self.common.egui.debug_state.handle_device_added(&device); - self.common.egui.log_state.handle_device_added(&device); } } InputEvent::DeviceRemoved { device } => { @@ -185,8 +184,7 @@ impl State { } #[cfg(feature = "debug")] { - self.common.egui.debug_state.handle_device_added(&device); - self.common.egui.log_state.handle_device_added(&device); + self.common.egui.debug_state.handle_device_removed(&device); } } InputEvent::Keyboard { event, .. } => { @@ -234,8 +232,7 @@ impl State { } #[cfg(feature = "debug")] { - if data.common.seats.iter().position(|x| x == seat).unwrap() - == 0 + if data.common.seats().position(|x| x == seat).unwrap() == 0 && data.common.egui.active { if data.common.egui.debug_state.wants_keyboard() { @@ -250,18 +247,6 @@ impl State { .add(&handle); return FilterResult::Intercept(None); } - if data.common.egui.log_state.wants_keyboard() { - data.common.egui.log_state.handle_keyboard( - &handle, - state == KeyState::Pressed, - modifiers.clone(), - ); - userdata - .get::() - .unwrap() - .add(&handle); - return FilterResult::Intercept(None); - } } } @@ -372,7 +357,7 @@ impl State { output.current_transform(), &output.geometry().size.to_f64(), ), - &self.common.start_time, + self.common.clock.now(), ) { session.cursor_info(seat, InputType::Pointer, geometry, offset); } @@ -388,15 +373,11 @@ impl State { ); #[cfg(feature = "debug")] - if self.common.seats.iter().position(|x| x == seat).unwrap() == 0 { + if self.common.seats().position(|x| x == seat).unwrap() == 0 { self.common .egui .debug_state .handle_pointer_motion(position.to_i32_round()); - self.common - .egui - .log_state - .handle_pointer_motion(position.to_i32_round()); } break; } @@ -410,8 +391,11 @@ impl State { if devices.has_device(&device) { let output = seat.active_output(); let geometry = output.geometry(); - let position = - geometry.loc.to_f64() + event.position_transformed(geometry.size); + let position = geometry.loc.to_f64() + + smithay::backend::input::AbsolutePositionEvent::position_transformed( + &event, + geometry.size, + ); let relative_pos = self.common.shell.map_global_to_space(position, &output); let workspace = self.common.shell.active_space_mut(&output); let serial = SERIAL_COUNTER.next_serial(); @@ -433,15 +417,11 @@ impl State { ); #[cfg(feature = "debug")] - if self.common.seats.iter().position(|x| x == seat).unwrap() == 0 { + if self.common.seats().position(|x| x == seat).unwrap() == 0 { self.common .egui .debug_state .handle_pointer_motion(position.to_i32_round()); - self.common - .egui - .log_state - .handle_pointer_motion(position.to_i32_round()); } break; } @@ -456,7 +436,7 @@ impl State { let devices = userdata.get::().unwrap(); if devices.has_device(&device) { #[cfg(feature = "debug")] - if self.common.seats.iter().position(|x| x == seat).unwrap() == 0 + if self.common.seats().position(|x| x == seat).unwrap() == 0 && self.common.egui.active { if self.common.egui.debug_state.wants_pointer() { @@ -464,17 +444,6 @@ impl State { self.common.egui.debug_state.handle_pointer_button( button, event.state() == ButtonState::Pressed, - self.common.egui.modifiers.clone(), - ); - } - break; - } - if self.common.egui.log_state.wants_pointer() { - if let Some(button) = event.button() { - self.common.egui.log_state.handle_pointer_button( - button, - event.state() == ButtonState::Pressed, - self.common.egui.modifiers.clone(), ); } break; @@ -575,7 +544,7 @@ impl State { let device = event.device(); for seat in self.common.seats().cloned().collect::>().iter() { #[cfg(feature = "debug")] - if self.common.seats.iter().position(|x| x == seat).unwrap() == 0 + if self.common.seats().position(|x| x == seat).unwrap() == 0 && self.common.egui.active { if self.common.egui.debug_state.wants_pointer() { @@ -591,19 +560,6 @@ impl State { ); break; } - if self.common.egui.log_state.wants_pointer() { - self.common.egui.log_state.handle_pointer_axis( - event - .amount_discrete(Axis::Horizontal) - .or_else(|| event.amount(Axis::Horizontal).map(|x| x * 3.0)) - .unwrap_or(0.0), - event - .amount_discrete(Axis::Vertical) - .or_else(|| event.amount(Axis::Vertical).map(|x| x * 3.0)) - .unwrap_or(0.0), - ); - break; - } } let userdata = seat.user_data(); diff --git a/src/logger/mod.rs b/src/logger/mod.rs index 4818c7da..14793437 100644 --- a/src/logger/mod.rs +++ b/src/logger/mod.rs @@ -1,104 +1,16 @@ // SPDX-License-Identifier: GPL-3.0-only -#[cfg(feature = "debug")] -use std::{ - collections::VecDeque, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, Mutex, - }, -}; - use anyhow::Result; use slog::Drain; -#[cfg(feature = "debug")] -mod serializer; - -#[cfg(feature = "debug")] -const MAX_RECORDS: usize = 1000; - -#[cfg(feature = "debug")] -pub type LogBuffer = Arc>>; - -#[cfg(feature = "debug")] -#[derive(Clone)] -struct DebugDrain { - buffer: LogBuffer, - dirty_flag: Arc, -} - pub struct LogState { _guard: slog_scope::GlobalLoggerGuard, - #[cfg(feature = "debug")] - pub dirty_flag: Arc, - #[cfg(feature = "debug")] - pub debug_buffer: LogBuffer, -} - -#[cfg(feature = "debug")] -pub struct OwnedRecord { - pub message: String, - pub level: slog::Level, - pub kv: serde_json::map::Map, -} - -#[cfg(feature = "debug")] -impl DebugDrain { - fn new() -> (DebugDrain, LogBuffer, Arc) { - let dirty_flag = Arc::new(AtomicBool::new(false)); - let buffer = Arc::new(Mutex::new(VecDeque::new())); - ( - DebugDrain { - buffer: buffer.clone(), - dirty_flag: dirty_flag.clone(), - }, - buffer, - dirty_flag, - ) - } -} - -#[cfg(feature = "debug")] -impl Drain for DebugDrain { - type Ok = (); - type Err = slog::Error; - - fn log( - &self, - record: &slog::Record<'_>, - values: &slog::OwnedKVList, - ) -> Result { - use serde_json::value::{Serializer as ValueSerializer, Value}; - use serializer::SerdeSerializer; - use slog::KV; - - let mut serializer = SerdeSerializer::start(ValueSerializer, None)?; - values.serialize(record, &mut serializer)?; - record.kv().serialize(record, &mut serializer)?; - let value = match serializer.end().map_err(|_| slog::Error::Other)? { - Value::Object(map) => map, - _ => unreachable!(), - }; - - let mut buffer = self.buffer.lock().unwrap(); - buffer.push_front(OwnedRecord { - message: format!("{}", record.msg()), - level: record.level(), - kv: value, - }); - buffer.truncate(MAX_RECORDS); - self.dirty_flag.store(true, Ordering::SeqCst); - - Ok(()) - } } pub fn init_logger() -> Result { let decorator = slog_term::TermDecorator::new().stderr().build(); // usually we would not want to use a Mutex here, but this is usefull for a prototype, // to make sure we do not miss any in-flight messages, when we crash. - #[cfg(not(feature = "debug"))] let logger = slog::Logger::root( std::sync::Mutex::new( slog_term::CompactFormat::new(decorator) @@ -108,21 +20,6 @@ pub fn init_logger() -> Result { .fuse(), slog::o!(), ); - #[cfg(feature = "debug")] - let (debug_drain, debug_buffer, dirty_flag) = DebugDrain::new(); - #[cfg(feature = "debug")] - let logger = slog::Logger::root( - slog::Duplicate::new( - std::sync::Mutex::new( - slog_term::CompactFormat::new(decorator) - .build() - .ignore_res(), - ), - debug_drain, - ) - .fuse(), - slog::o!(), - ); let _guard = slog_scope::set_global_logger(logger); slog_stdlog::init().unwrap(); @@ -135,11 +32,5 @@ pub fn init_logger() -> Result { ); } - Ok(LogState { - _guard, - #[cfg(feature = "debug")] - debug_buffer, - #[cfg(feature = "debug")] - dirty_flag, - }) + Ok(LogState { _guard }) } diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index f39cafb0..8377ff90 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -308,7 +308,7 @@ impl PointerTarget for CosmicStack { for session in &*sessions.0.borrow() { let buffer_loc = (event.location.x, event.location.y); // we always screencast windows at 1x1 scale if let Some((geo, hotspot)) = - seat.cursor_geometry(buffer_loc, &data.common.start_time) + seat.cursor_geometry(buffer_loc, data.common.clock.now()) { session.cursor_info(seat, InputType::Pointer, geo, hotspot); } diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 9df7f253..c65ca09c 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -206,7 +206,7 @@ impl PointerTarget for CosmicWindow { for session in &*sessions.0.borrow() { let buffer_loc = (event.location.x, event.location.y); // we always screencast windows at 1x1 scale if let Some((geo, hotspot)) = - seat.cursor_geometry(buffer_loc, &data.common.start_time) + seat.cursor_geometry(buffer_loc, data.common.clock.now()) { session.cursor_info(seat, InputType::Pointer, geo, hotspot); } diff --git a/src/shell/layout/floating/grabs/moving.rs b/src/shell/layout/floating/grabs/moving.rs index 02b52713..a04cf043 100644 --- a/src/shell/layout/floating/grabs/moving.rs +++ b/src/shell/layout/floating/grabs/moving.rs @@ -51,7 +51,8 @@ impl MoveGrabState { } let scale = output.current_scale().fractional_scale().into(); - self.window.render_elements::( + AsRenderElements::::render_elements::( + &self.window, (location.to_i32_round() - output.geometry().loc - self.window.geometry().loc) .to_physical_precise_round(scale), scale, diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index 4f4a3755..3760bfd3 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -1243,7 +1243,8 @@ impl TilingLayout { } }) .flat_map(|(mapped, loc)| { - mapped.render_elements::>( + AsRenderElements::::render_elements::>( + mapped, loc.to_physical_precise_round(output_scale) - mapped .geometry() diff --git a/src/state.rs b/src/state.rs index 16a4e159..9b996f56 100644 --- a/src/state.rs +++ b/src/state.rs @@ -19,7 +19,10 @@ use smithay::{ drm::DrmNode, renderer::element::{default_primary_scanout_output_compare, RenderElementStates}, }, - desktop::utils::{surface_primary_scanout_output, update_surface_primary_scanout_output}, + desktop::utils::{ + surface_presentation_feedback_flags_from_states, surface_primary_scanout_output, + update_surface_primary_scanout_output, OutputPresentationFeedback, + }, input::{Seat, SeatState}, output::{Mode as OutputMode, Output, Scale}, reexports::{ @@ -30,20 +33,18 @@ use smithay::{ Display, DisplayHandle, }, }, + utils::{Clock, Monotonic, Rectangle}, wayland::{ compositor::CompositorState, data_device::DataDeviceState, dmabuf::DmabufState, keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitState, output::OutputManagerState, - primary_selection::PrimarySelectionState, shm::ShmState, viewporter::ViewporterState, + presentation::PresentationState, primary_selection::PrimarySelectionState, shm::ShmState, + viewporter::ViewporterState, }, }; -use std::{ - cell::RefCell, - ffi::OsString, - time::{Duration, Instant}, -}; +use std::{cell::RefCell, ffi::OsString, time::Duration}; #[cfg(feature = "debug")] -use std::{collections::VecDeque, time::Duration}; +use std::{collections::VecDeque, time::Instant}; pub struct ClientState { pub workspace_client_state: WorkspaceClientState, @@ -79,7 +80,7 @@ pub struct Common { seats: Vec>, last_active_seat: Option>, - pub start_time: Instant, + pub clock: Clock, pub should_stop: bool, pub log: LogState, @@ -93,6 +94,7 @@ pub struct Common { pub keyboard_shortcuts_inhibit_state: KeyboardShortcutsInhibitState, pub output_state: OutputManagerState, pub output_configuration_state: OutputConfigurationState, + pub presentation_state: PresentationState, pub primary_selection_state: PrimarySelectionState, pub screencopy_state: ScreencopyState, pub seat_state: SeatState, @@ -104,8 +106,6 @@ pub struct Common { #[cfg(feature = "debug")] pub struct Egui { pub debug_state: smithay_egui::EguiState, - pub log_state: smithay_egui::EguiState, - pub modifiers: smithay::wayland::seat::ModifiersState, pub active: bool, pub alpha: f32, } @@ -113,11 +113,19 @@ pub struct Egui { #[cfg(feature = "debug")] pub struct Fps { pub state: smithay_egui::EguiState, - pub modifiers: smithay::wayland::seat::ModifiersState, pub frames: VecDeque<(Instant, Duration)>, pub start: Instant, } +#[cfg(feature = "debug")] +pub struct Frame { + start: Instant, + duration_elements: Duration, + duration_render: Duration, + duration_screencopy: Duration, + duration_displayed: Duration, +} + pub enum BackendData { X11(X11State), Winit(WinitState), @@ -204,7 +212,7 @@ impl BackendData { // Swapping with damage (which should be empty on these frames) is likely good enough anyway. BackendData::X11(ref mut state) => state.schedule_render(output, screencopy), BackendData::Kms(ref mut state) => { - if let Err(err) = state.schedule_render(loop_handle, output, screencopy) { + if let Err(err) = state.schedule_render(loop_handle, output, None, screencopy) { slog_scope::crit!("Failed to schedule event, are we shutting down? {:?}", err); } } @@ -221,6 +229,7 @@ impl State { signal: LoopSignal, log: LogState, ) -> State { + let clock = Clock::new().expect("Failed to initialize clock"); let config = Config::load(); let compositor_state = CompositorState::new::(dh, None); let data_device_state = DataDeviceState::new::(dh, None); @@ -228,6 +237,7 @@ impl State { let keyboard_shortcuts_inhibit_state = KeyboardShortcutsInhibitState::new::(dh); let output_state = OutputManagerState::new_with_xdg_output::(dh); let output_configuration_state = OutputConfigurationState::new(dh, |_| true); + let presentation_state = PresentationState::new::(dh, clock.id() as u32); let primary_selection_state = PrimarySelectionState::new::(dh, None); let screencopy_state = ScreencopyState::new::( dh, @@ -258,20 +268,16 @@ impl State { seats: Vec::new(), last_active_seat: None, - start_time: Instant::now(), + clock, should_stop: false, log, #[cfg(feature = "debug")] egui: Egui { - debug_state: smithay_egui::EguiState::new(smithay_egui::EguiMode::Continuous), - log_state: { - let mut state = - smithay_egui::EguiState::new(smithay_egui::EguiMode::Continuous); - state.set_zindex(0); - state - }, - modifiers: Default::default(), + debug_state: smithay_egui::EguiState::new(Rectangle::from_loc_and_size( + (0, 0), + (400, 800), + )), active: false, alpha: 1.0, }, @@ -285,6 +291,7 @@ impl State { keyboard_shortcuts_inhibit_state, output_state, output_configuration_state, + presentation_state, primary_selection_state, viewporter_state, wl_drm_state, @@ -365,7 +372,7 @@ impl Common { } pub fn send_frames(&self, output: &Output, render_element_states: &RenderElementStates) { - let time = self.start_time.elapsed(); + let time = self.clock.now(); let throttle = Some(Duration::from_secs(1)); let active = self.shell.active_space(output); @@ -413,11 +420,43 @@ impl Common { layer_surface.send_frame(output, time, throttle, surface_primary_scanout_output); } } + + pub fn take_presentation_feedback( + &self, + output: &Output, + render_element_states: &RenderElementStates, + ) -> OutputPresentationFeedback { + let mut output_presentation_feedback = OutputPresentationFeedback::new(output); + + let active = self.shell.active_space(output); + active.mapped().for_each(|mapped| { + mapped.active_window().take_presentation_feedback( + &mut output_presentation_feedback, + surface_primary_scanout_output, + |surface, _| { + surface_presentation_feedback_flags_from_states(surface, render_element_states) + }, + ); + }); + + let map = smithay::desktop::layer_map_for_output(output); + for layer_surface in map.layers() { + layer_surface.take_presentation_feedback( + &mut output_presentation_feedback, + surface_primary_scanout_output, + |surface, _| { + surface_presentation_feedback_flags_from_states(surface, render_element_states) + }, + ); + } + + output_presentation_feedback + } } #[cfg(feature = "debug")] impl Fps { - const WINDOW_SIZE: usize = 100; + const WINDOW_SIZE: usize = 1000; pub fn start(&mut self) { self.start = Instant::now(); @@ -478,14 +517,13 @@ impl Default for Fps { fn default() -> Fps { Fps { state: { - let mut state = smithay_egui::EguiState::new(smithay_egui::EguiMode::Continuous); + let state = + smithay_egui::EguiState::new(Rectangle::from_loc_and_size((0, 0), (400, 800))); let mut visuals: egui::style::Visuals = Default::default(); visuals.window_shadow.extrusion = 0.0; state.context().set_visuals(visuals); - state.set_zindex(110); // always render on top state }, - modifiers: Default::default(), frames: VecDeque::with_capacity(Fps::WINDOW_SIZE + 1), start: Instant::now(), } diff --git a/src/utils/prelude.rs b/src/utils/prelude.rs index 18deee0b..57348025 100644 --- a/src/utils/prelude.rs +++ b/src/utils/prelude.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, sync::Mutex}; +use std::{cell::RefCell, sync::Mutex, time::Duration}; use crate::{ backend::render::cursor::CursorState, @@ -11,7 +11,7 @@ use smithay::{ Seat, }, output::Output, - utils::{Buffer, IsAlive, Logical, Point, Rectangle, Transform}, + utils::{Buffer, IsAlive, Logical, Monotonic, Point, Rectangle, Time, Transform}, wayland::compositor::with_states, }; @@ -47,7 +47,7 @@ pub trait SeatExt { fn cursor_geometry( &self, loc: impl Into>, - start_time: &std::time::Instant, + time: Time, ) -> Option<(Rectangle, Point)>; } @@ -75,7 +75,7 @@ impl SeatExt for Seat { fn cursor_geometry( &self, loc: impl Into>, - start_time: &std::time::Instant, + time: Time, ) -> Option<(Rectangle, Point)> { let location = loc.into().to_i32_round(); @@ -117,7 +117,7 @@ impl SeatExt for Seat { let state = seat_userdata.get::().unwrap(); let frame = state .cursor - .get_image(1, start_time.elapsed().as_millis() as u32); + .get_image(1, Into::::into(time).as_millis() as u32); Some(( Rectangle::from_loc_and_size( diff --git a/src/wayland/handlers/mod.rs b/src/wayland/handlers/mod.rs index 5f93b207..6b6028d5 100644 --- a/src/wayland/handlers/mod.rs +++ b/src/wayland/handlers/mod.rs @@ -4,11 +4,11 @@ pub mod buffer; pub mod compositor; pub mod data_device; pub mod dmabuf; -//pub mod export_dmabuf; pub mod keyboard_shortcuts_inhibit; pub mod layer_shell; pub mod output; pub mod output_configuration; +pub mod presentation; pub mod primary_selection; pub mod screencopy; pub mod seat; diff --git a/src/wayland/handlers/presentation.rs b/src/wayland/handlers/presentation.rs new file mode 100644 index 00000000..96dd14e7 --- /dev/null +++ b/src/wayland/handlers/presentation.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use crate::state::State; +use smithay::delegate_presentation; + +delegate_presentation!(State); diff --git a/src/wayland/handlers/screencopy.rs b/src/wayland/handlers/screencopy.rs index 540be69c..9bab636b 100644 --- a/src/wayland/handlers/screencopy.rs +++ b/src/wayland/handlers/screencopy.rs @@ -19,7 +19,8 @@ use smithay::{ element::{ surface::WaylandSurfaceRenderElement, AsRenderElements, RenderElementStates, }, - gles2::{Gles2Renderbuffer, Gles2Renderer}, + gles2::Gles2Renderbuffer, + glow::GlowRenderer, Bind, BufferType, ExportMem, ImportAll, Offscreen, Renderer, }, }, @@ -471,23 +472,6 @@ fn node_from_params( } } -fn prepare_renderer( - renderer: &mut R, - buffer: &WlBuffer, -) -> Result<(), ::Error> -where - R: Bind + Offscreen, -{ - if let Ok(dmabuf) = get_dmabuf(buffer) { - renderer.bind(dmabuf)?; - } else { - let size = buffer_dimensions(buffer).unwrap(); - let render_buffer = renderer.create_buffer(size)?; - renderer.bind(render_buffer)?; - }; - Ok(()) -} - fn submit_buffer( session: &Session, buffer: &WlBuffer, @@ -537,7 +521,7 @@ where Ok(()) } -pub fn render_to_buffer( +pub fn render_session( node: Option, renderer: &mut R, session: &Session, @@ -546,9 +530,10 @@ pub fn render_to_buffer( render_fn: F, ) -> Result> where - R: Bind + Offscreen + ExportMem, + R: ExportMem, F: FnOnce( Option<&DrmNode>, + &WlBuffer, &mut R, &mut DamageTrackedRenderer, usize, @@ -557,15 +542,19 @@ where DamageTrackedRendererError, >, { - prepare_renderer(renderer, ¶ms.buffer).map_err(DamageTrackedRendererError::Rendering)?; - let mut dtr = session .user_data() .get::() .unwrap() .borrow_mut(); - let res = render_fn(node.as_ref(), renderer, &mut *dtr, params.age as usize)?; + let res = render_fn( + node.as_ref(), + ¶ms.buffer, + renderer, + &mut *dtr, + params.age as usize, + )?; if let (Some(damage), _) = res { submit_buffer(session, ¶ms.buffer, renderer, transform, damage) @@ -607,28 +596,50 @@ pub fn render_output_to_buffer( }; let common = &mut state.common; - render_to_buffer::<_, _, Gles2Renderbuffer>( + render_session::<_, _>( node, renderer, session, ¶ms, output.current_transform(), - |node, renderer, dtr, age| { - render_output::<_, Gles2Renderbuffer, Dmabuf>( - node, - renderer, - dtr, - age, - common, - &output, - match session.cursor_mode() { - ScreencopyCursorMode::Embedded => CursorMode::All, - ScreencopyCursorMode::Captured(_) | ScreencopyCursorMode::None => { - CursorMode::None - } - }, - None, - ) + |node, buffer, renderer, dtr, age| { + let cursor_mode = match session.cursor_mode() { + ScreencopyCursorMode::Embedded => CursorMode::All, + ScreencopyCursorMode::Captured(_) | ScreencopyCursorMode::None => CursorMode::None, + }; + + if let Ok(dmabuf) = get_dmabuf(buffer) { + render_output::<_, _, Gles2Renderbuffer, Dmabuf>( + node, + renderer, + dmabuf, + dtr, + age, + common, + &output, + cursor_mode, + None, + #[cfg(feature = "debug")] + None, + ) + } else { + let size = buffer_dimensions(buffer).unwrap(); + let render_buffer = Offscreen::::create_buffer(renderer, size) + .map_err(DamageTrackedRendererError::Rendering)?; + render_output::<_, _, Gles2Renderbuffer, Dmabuf>( + node, + renderer, + render_buffer, + dtr, + age, + common, + &output, + cursor_mode, + None, + #[cfg(feature = "debug")] + None, + ) + } }, ) .map_err(|err| (FailureReason::Unspec, err.into())) @@ -666,29 +677,51 @@ pub fn render_workspace_to_buffer( }; let common = &mut state.common; - render_to_buffer::<_, _, Gles2Renderbuffer>( + render_session::<_, _>( node, renderer, session, ¶ms, output.current_transform(), - |node, renderer, dtr, age| { - render_workspace::<_, Gles2Renderbuffer, Dmabuf>( - node, - renderer, - dtr, - age, - common, - &output, - handle, - match session.cursor_mode() { - ScreencopyCursorMode::Embedded => CursorMode::All, - ScreencopyCursorMode::Captured(_) | ScreencopyCursorMode::None => { - CursorMode::None - } - }, - None, - ) + |node, buffer, renderer, dtr, age| { + let cursor_mode = match session.cursor_mode() { + ScreencopyCursorMode::Embedded => CursorMode::All, + ScreencopyCursorMode::Captured(_) | ScreencopyCursorMode::None => CursorMode::None, + }; + if let Ok(dmabuf) = get_dmabuf(buffer) { + render_workspace::<_, _, Gles2Renderbuffer, Dmabuf>( + node, + renderer, + dmabuf, + dtr, + age, + common, + &output, + handle, + cursor_mode, + None, + #[cfg(feature = "debug")] + None, + ) + } else { + let size = buffer_dimensions(buffer).unwrap(); + let render_buffer = Offscreen::::create_buffer(renderer, size) + .map_err(DamageTrackedRendererError::Rendering)?; + render_workspace::<_, _, Gles2Renderbuffer, Dmabuf>( + node, + renderer, + render_buffer, + dtr, + age, + common, + &output, + handle, + cursor_mode, + None, + #[cfg(feature = "debug")] + None, + ) + } }, ) .map_err(|err| (FailureReason::Unspec, err.into())) @@ -728,16 +761,16 @@ pub fn render_window_to_buffer( _ => unreachable!(), }; - render_to_buffer::<_, _, Gles2Renderbuffer>( + render_session::<_, _>( node, renderer, session, ¶ms, Transform::Normal, - |_node, renderer, dtr, age| { + |_node, buffer, renderer, dtr, age| { // TODO cursor elements! - let mut elements = AsRenderElements::::render_elements::< - WindowCaptureElement, + let mut elements = AsRenderElements::::render_elements::< + WindowCaptureElement, >( window, (-geometry.loc.x, -geometry.loc.y).into(), @@ -771,7 +804,7 @@ pub fn render_window_to_buffer( seat, location, 1.0.into(), - &state.common.start_time, + state.common.clock.now(), true, ) .into_iter() @@ -789,6 +822,19 @@ pub fn render_window_to_buffer( } } + if let Ok(dmabuf) = get_dmabuf(buffer) { + renderer + .bind(dmabuf) + .map_err(DamageTrackedRendererError::Rendering)?; + } else { + let size = buffer_dimensions(buffer).unwrap(); + let render_buffer = Offscreen::::create_buffer(renderer, size) + .map_err(DamageTrackedRendererError::Rendering)?; + renderer + .bind(render_buffer) + .map_err(DamageTrackedRendererError::Rendering)?; + } + dtr.render_output(renderer, age, &elements, CLEAR_COLOR, None) }, )