atagen

monolith

public
Star0
Watch0

Commit 1136328fbe69

tarball

atagen <boss@atagen.co>2026-10-19 10:44 UTC

1 file changed+55-34
drm: move adaptive sync and frame timing out of render_surface

render_surface carried about 175 lines, including two blocks that only touch Surface. They are now Surface::set_vrr and Surface::record_frame.
Modified file: crates/monolith/src/drm/render.rs+55 additions−34 deletions
@@ -1,5 +1,6 @@
11 //! Drawing one output and queueing the frame.
22
3+use super::Surface;
34 use super::pacing::{schedule, watch};
45 use crate::backend::Backend;
56 use crate::frame::{CLEAR, FrameCtx};
@@ -42,6 +43,51 @@ pub fn render(state: &mut State) {
4243 }
4344 }
4445
46+struct FrameTimes {
47+ took_ns: u64,
48+ scene_ns: u64,
49+ render_ns: u64,
50+ render_cpu_ns: u64,
51+ queue_ns: u64,
52+ frame_cpu_ns: u64,
53+}
54+
55+impl Surface {
56+ fn set_vrr(&mut self, want: bool) {
57+ if want == self.vrr_on {
58+ return;
59+ }
60+ match self.drm.with_compositor(|c| c.use_vrr(want)) {
61+ Ok(()) => {
62+ self.vrr_on = want;
63+ tracing::info!(output = self.output.name(), vrr = want, "adaptive sync");
64+ }
65+ Err(e) => {
66+ tracing::warn!("could not switch adaptive sync: {e}");
67+ self.vrr_capable = false;
68+ }
69+ }
70+ }
71+
72+ fn record_frame(&mut self, t: FrameTimes) {
73+ self.budget.record(t.took_ns);
74+ if self.frame_cpu_ns.len() < MAX_CPU_SAMPLES {
75+ self.frame_cpu_ns.push(t.frame_cpu_ns);
76+ }
77+ if t.took_ns > self.refresh_ns / 2 {
78+ tracing::debug!(
79+ output = self.output.name(),
80+ took_ns = t.took_ns,
81+ scene_ns = t.scene_ns,
82+ render_ns = t.render_ns,
83+ render_cpu_ns = t.render_cpu_ns,
84+ queue_ns = t.queue_ns,
85+ "slow frame"
86+ );
87+ }
88+ }
89+}
90+
4591 /// Draws one output for the vblank at `target`. Returns whether it still
4692 /// animates without a frame in flight, and so needs another render.
4793 #[profiling::function]
@@ -84,22 +130,7 @@ pub(super) fn render_surface(state: &mut State, crtc: crtc::Handle, target: u64)
84130 Vrr::On => true,
85131 Vrr::Fullscreen => fullscreen.is_some(),
86132 });
87- if want_vrr != surface.vrr_on {
88- match surface.drm.with_compositor(|c| c.use_vrr(want_vrr)) {
89- Ok(()) => {
90- surface.vrr_on = want_vrr;
91- tracing::info!(
92- output = surface.output.name(),
93- vrr = want_vrr,
94- "adaptive sync"
95- );
96- }
97- Err(e) => {
98- tracing::warn!("could not switch adaptive sync: {e}");
99- surface.vrr_capable = false;
100- }
101- }
102- }
133+ surface.set_vrr(want_vrr);
103134 let began = std::time::Instant::now();
104135 let frame_cpu_began = thread_cpu_ns();
105136 let now = target;
@@ -182,24 +213,14 @@ pub(super) fn render_surface(state: &mut State, crtc: crtc::Handle, target: u64)
182213 tracing::warn!("rendering failed: {e}");
183214 }
184215 }
185- let render_cpu_ns = thread_cpu_ns() - cpu_began;
186- let render_ns = render_began.elapsed().as_nanos() as u64 - queue_ns;
187- let took = began.elapsed().as_nanos() as u64;
188- surface.budget.record(took);
189- if surface.frame_cpu_ns.len() < MAX_CPU_SAMPLES {
190- surface.frame_cpu_ns.push(thread_cpu_ns() - frame_cpu_began);
191- }
192- if took > surface.refresh_ns / 2 {
193- tracing::debug!(
194- output = surface.output.name(),
195- took_ns = took,
196- scene_ns,
197- render_ns,
198- render_cpu_ns,
199- queue_ns,
200- "slow frame"
201- );
202- }
216+ surface.record_frame(FrameTimes {
217+ took_ns: began.elapsed().as_nanos() as u64,
218+ scene_ns,
219+ render_ns: render_began.elapsed().as_nanos() as u64 - queue_ns,
220+ render_cpu_ns: thread_cpu_ns() - cpu_began,
221+ queue_ns,
222+ frame_cpu_ns: thread_cpu_ns() - frame_cpu_began,
223+ });
203224 if !surface.queued {
204225 surface.target_ns = None;
205226 }