Skip to content

Commit

Permalink
Merge pull request #223 from mahkoh/jorth/damage-tracking
Browse files Browse the repository at this point in the history
Implement damage tracking
  • Loading branch information
mahkoh authored Jul 13, 2024
2 parents 579f93d + bb66abb commit 618fdfe
Show file tree
Hide file tree
Showing 68 changed files with 1,533 additions and 403 deletions.
1 change: 0 additions & 1 deletion docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,5 @@ Jay supports the following wayland protocols:

The following features are currently not supported but might get implemented in the future:

- Fine-grained damage tracking.
- Touch support.
- Tearing updates of fullscreen games.
2 changes: 2 additions & 0 deletions release-notes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Unreleased

- Add fine-grained damage tracking.

# 1.4.0 (2024-07-07)

- Add window management mode.
Expand Down
15 changes: 15 additions & 0 deletions src/async_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub use {crate::async_engine::ae_yield::Yield, ae_task::SpawnedFuture};
use {
crate::{
async_engine::ae_task::Runnable,
time::Time,
utils::{array, numcell::NumCell, syncqueue::SyncQueue},
},
std::{
Expand Down Expand Up @@ -33,6 +34,7 @@ pub struct AsyncEngine {
stash: RefCell<VecDeque<Runnable>>,
yield_stash: RefCell<VecDeque<Waker>>,
stopped: Cell<bool>,
now: Cell<Option<Time>>,
}

impl AsyncEngine {
Expand All @@ -45,6 +47,7 @@ impl AsyncEngine {
stash: Default::default(),
yield_stash: Default::default(),
stopped: Cell::new(false),
now: Default::default(),
})
}

Expand Down Expand Up @@ -84,6 +87,7 @@ impl AsyncEngine {
let mut stash = self.stash.borrow_mut();
let mut yield_stash = self.yield_stash.borrow_mut();
while self.num_queued.get() > 0 {
self.now.take();
self.iteration.fetch_add(1);
let mut phase = 0;
while phase < NUM_PHASES {
Expand Down Expand Up @@ -119,4 +123,15 @@ impl AsyncEngine {
fn iteration(&self) -> u64 {
self.iteration.get()
}

pub fn now(&self) -> Time {
match self.now.get() {
Some(t) => t,
None => {
let now = Time::now_unchecked();
self.now.set(Some(now));
now
}
}
}
}
3 changes: 1 addition & 2 deletions src/backends/metal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ use {
},
logind::{LogindError, Session},
state::State,
time::now_usec,
udev::{Udev, UdevError, UdevMonitor},
utils::{
clonecell::{CloneCell, UnsafeCellCloneSafe},
Expand Down Expand Up @@ -469,7 +468,7 @@ impl MetalInputDevice {
}

fn pre_pause(&self) {
let time_usec = now_usec();
let time_usec = self.state.now_usec();
for (key, _) in self.pressed_keys.take() {
self.event(InputEvent::Key {
time_usec,
Expand Down
13 changes: 7 additions & 6 deletions src/backends/metal/video.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use {
renderer::RenderResult,
state::State,
theme::Color,
time::now_nsec,
tree::OutputNode,
udev::UdevDevice,
utils::{
Expand Down Expand Up @@ -591,7 +590,7 @@ impl MetalConnector {
});
if let Some(delta) = *DELTA {
let next_present = self.next_flip_nsec.get().saturating_sub(delta);
if now_nsec() < next_present {
if self.state.now_nsec() < next_present {
self.state.ring.timeout(next_present).await.unwrap();
}
}
Expand Down Expand Up @@ -836,6 +835,7 @@ impl MetalConnector {
render_hw_cursor,
output.has_fullscreen(),
output.global.persistent.transform.get(),
Some(&self.state.damage_visualizer),
);
let try_direct_scanout = try_direct_scanout
&& self.direct_scanout_enabled()
Expand Down Expand Up @@ -923,9 +923,10 @@ impl MetalConnector {
if let Some(node) = self.state.root.outputs.get(&self.connector_id) {
let buffer = &buffers[self.next_buffer.get() % buffers.len()];
let mut rr = self.render_result.borrow_mut();
rr.output_id = node.id;
let fb =
self.prepare_present_fb(&mut rr, buffer, &plane, &node, try_direct_scanout)?;
rr.dispatch_frame_requests();
rr.dispatch_frame_requests(self.state.now_msec());
let (crtc_x, crtc_y, crtc_w, crtc_h, src_width, src_height) =
match &fb.direct_scanout_data {
None => {
Expand Down Expand Up @@ -2172,9 +2173,9 @@ impl MetalBackend {
_ => return,
};
connector.can_present.set(true);
connector
.active_framebuffer
.set(connector.next_framebuffer.take());
if let Some(fb) = connector.next_framebuffer.take() {
connector.active_framebuffer.set(Some(fb));
}
if connector.has_damage.get() || connector.cursor_changed.get() {
connector.schedule_present();
}
Expand Down
11 changes: 5 additions & 6 deletions src/backends/x.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use {
gfx_api::{GfxContext, GfxError, GfxFramebuffer, GfxTexture},
renderer::RenderResult,
state::State,
time::now_usec,
utils::{
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell,
queue::AsyncQueue, syncqueue::SyncQueue,
Expand Down Expand Up @@ -818,7 +817,7 @@ impl XBackend {
inverted: false,
});
seat.mouse_event(InputEvent::AxisFrame {
time_usec: now_usec(),
time_usec: self.state.now_usec(),
});
}
} else {
Expand All @@ -834,7 +833,7 @@ impl XBackend {
n => BTN_SIDE + n - 8,
};
seat.mouse_event(InputEvent::Button {
time_usec: now_usec(),
time_usec: self.state.now_usec(),
button,
state,
});
Expand All @@ -851,7 +850,7 @@ impl XBackend {
let event: XiKeyPress = event.parse()?;
if let Some(seat) = self.seats.get(&event.deviceid) {
seat.kb_event(InputEvent::Key {
time_usec: now_usec(),
time_usec: self.state.now_usec(),
key: event.detail - 8,
state,
});
Expand Down Expand Up @@ -885,7 +884,7 @@ impl XBackend {
self.mouse_seats.get(&event.deviceid),
) {
seat.mouse_event(InputEvent::ConnectorPosition {
time_usec: now_usec(),
time_usec: self.state.now_usec(),
connector: win.id,
x: Fixed::from_1616(event.event_x),
y: Fixed::from_1616(event.event_y),
Expand All @@ -904,7 +903,7 @@ impl XBackend {
_ => return Ok(()),
};
seat.mouse_event(InputEvent::ConnectorPosition {
time_usec: now_usec(),
time_usec: self.state.now_nsec(),
connector: win.id,
x: Fixed::from_1616(event.event_x),
y: Fixed::from_1616(event.event_y),
Expand Down
9 changes: 8 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
mod color;
mod damage_tracking;
mod duration;
mod generate;
mod idle;
mod input;
Expand All @@ -12,7 +15,7 @@ mod unlock;

use {
crate::{
cli::{input::InputArgs, randr::RandrArgs},
cli::{damage_tracking::DamageTrackingArgs, input::InputArgs, randr::RandrArgs},
compositor::start_compositor,
portal,
},
Expand Down Expand Up @@ -65,6 +68,9 @@ pub enum Cmd {
Randr(RandrArgs),
/// Inspect/modify input settings.
Input(InputArgs),
/// Modify damage tracking settings. (Only for debugging.)
#[clap(hide = true)]
DamageTracking(DamageTrackingArgs),
#[cfg(feature = "it")]
RunTests,
}
Expand Down Expand Up @@ -241,6 +247,7 @@ pub fn main() {
Cmd::Portal => portal::run_freestanding(cli.global),
Cmd::Randr(a) => randr::main(cli.global, a),
Cmd::Input(a) => input::main(cli.global, a),
Cmd::DamageTracking(a) => damage_tracking::main(cli.global, a),
#[cfg(feature = "it")]
Cmd::RunTests => crate::it::run_tests(),
}
Expand Down
36 changes: 36 additions & 0 deletions src/cli/color.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use {
crate::{theme::Color, utils::errorfmt::ErrorFmt},
std::ops::Range,
};

pub fn parse_color(string: &str) -> Color {
let hex = match string.strip_prefix("#") {
Some(s) => s,
_ => fatal!("Color must start with #"),
};
let d = |range: Range<usize>| match u8::from_str_radix(&hex[range.clone()], 16) {
Ok(n) => n,
Err(e) => {
fatal!(
"Could not parse color component {}: {}",
&hex[range],
ErrorFmt(e)
)
}
};
let s = |range: Range<usize>| {
let v = d(range);
(v << 4) | v
};
let (r, g, b, a) = match hex.len() {
3 => (s(0..1), s(1..2), s(2..3), u8::MAX),
4 => (s(0..1), s(1..2), s(2..3), s(3..4)),
6 => (d(0..2), d(2..4), d(4..6), u8::MAX),
8 => (d(0..2), d(2..4), d(4..6), d(6..8)),
_ => fatal!(
"Unexpected length of color string (should be 3, 4, 6, or 8): {}",
hex.len()
),
};
jay_config::theme::Color::new_straight(r, g, b, a).into()
}
106 changes: 106 additions & 0 deletions src/cli/damage_tracking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use {
crate::{
cli::{color::parse_color, duration::parse_duration, GlobalArgs},
tools::tool_client::{with_tool_client, ToolClient},
wire::jay_damage_tracking::{SetVisualizerColor, SetVisualizerDecay, SetVisualizerEnabled},
},
clap::{Args, Subcommand},
std::rc::Rc,
};

#[derive(Args, Debug)]
pub struct DamageTrackingArgs {
#[clap(subcommand)]
pub command: DamageTrackingCmd,
}

#[derive(Subcommand, Debug)]
pub enum DamageTrackingCmd {
/// Visualize damage.
Show,
/// Hide damage.
Hide,
/// Set the color used for damage visualization.
SetColor(ColorArgs),
/// Set the amount of time damage is shown.
SetDecay(DecayArgs),
}

#[derive(Args, Debug)]
pub struct ColorArgs {
/// The color to visualize damage.
///
/// Should be specified in one of the following formats:
///
/// * `#rgb`
/// * `#rgba`
/// * `#rrggbb`
/// * `#rrggbbaa`
pub color: String,
}

#[derive(Args, Debug)]
pub struct DecayArgs {
/// The interval of inactivity after which to disable the screens.
///
/// Minutes, seconds, and milliseconds can be specified in any of the following formats:
///
/// * 1m
/// * 1m5s
/// * 1m 5s
/// * 1min 5sec
/// * 1 minute 5 seconds.
pub duration: Vec<String>,
}

pub fn main(global: GlobalArgs, damage_tracking_args: DamageTrackingArgs) {
with_tool_client(global.log_level.into(), |tc| async move {
let damage_tracking = Rc::new(DamageTracking { tc: tc.clone() });
damage_tracking.run(damage_tracking_args).await;
});
}

struct DamageTracking {
tc: Rc<ToolClient>,
}

impl DamageTracking {
async fn run(&self, args: DamageTrackingArgs) {
let tc = &self.tc;
let Some(dt) = tc.jay_damage_tracking().await else {
fatal!("Compositor does not support damage tracking");
};
match args.command {
DamageTrackingCmd::Show => {
tc.send(SetVisualizerEnabled {
self_id: dt,
enabled: 1,
});
}
DamageTrackingCmd::Hide => {
tc.send(SetVisualizerEnabled {
self_id: dt,
enabled: 0,
});
}
DamageTrackingCmd::SetColor(c) => {
let color = parse_color(&c.color);
tc.send(SetVisualizerColor {
self_id: dt,
r: color.r,
g: color.g,
b: color.b,
a: color.a,
});
}
DamageTrackingCmd::SetDecay(c) => {
let duration = parse_duration(&c.duration);
tc.send(SetVisualizerDecay {
self_id: dt,
millis: duration.as_millis() as _,
});
}
}
tc.round_trip().await;
}
}
Loading

0 comments on commit 618fdfe

Please sign in to comment.