Skip to content

Commit 46e5e48

Browse files
committed
some perf gains
1 parent 6264e29 commit 46e5e48

15 files changed

Lines changed: 258 additions & 221 deletions

File tree

frontends/rioterm/src/application.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,9 @@ impl ApplicationHandler<EventPayload> for Application<'_> {
244244
if self.config.renderer.disable_unfocused_render
245245
&& !route.window.is_focused
246246
{
247+
if route.window.screen.renderer.scrollbar.needs_redraw() {
248+
route.request_redraw();
249+
}
247250
return;
248251
}
249252

@@ -430,6 +433,8 @@ impl ApplicationHandler<EventPayload> for Application<'_> {
430433
} else {
431434
route.clear_errors();
432435
}
436+
437+
route.request_redraw();
433438
}
434439
}
435440
RioEventType::Rio(RioEvent::Exit | RioEvent::Quit) => {
@@ -585,7 +590,7 @@ impl ApplicationHandler<EventPayload> for Application<'_> {
585590
}
586591
}
587592
RioEventType::Rio(RioEvent::PrepareRenderOnRoute(millis, route_id)) => {
588-
let timer_id = TimerId::new(Topic::RenderRoute, route_id);
593+
let timer_id = TimerId::new(Topic::ScheduledRenderRoute, route_id);
589594
let event = EventPayload::new(
590595
RioEventType::Rio(RioEvent::RenderRoute(route_id)),
591596
window_id,
@@ -1352,7 +1357,7 @@ impl ApplicationHandler<EventPayload> for Application<'_> {
13521357
.assistant
13531358
.hover(mx, my, win_w, scale)
13541359
{
1355-
route.request_redraw();
1360+
route.request_overlay_redraw();
13561361
}
13571362

13581363
if route
@@ -1383,7 +1388,7 @@ impl ApplicationHandler<EventPayload> for Application<'_> {
13831388
.command_palette
13841389
.hover(mx, my, win_w, scale)
13851390
{
1386-
route.request_redraw();
1391+
route.request_overlay_redraw();
13871392
}
13881393
route.window.winit_window.set_cursor(CursorIcon::Default);
13891394
return;
@@ -1824,6 +1829,7 @@ impl ApplicationHandler<EventPayload> for Application<'_> {
18241829
false,
18251830
);
18261831
route.window.configure_window(&self.config);
1832+
route.request_redraw();
18271833
}
18281834

18291835
WindowEvent::DroppedFile(path) => {
@@ -1841,6 +1847,7 @@ impl ApplicationHandler<EventPayload> for Application<'_> {
18411847
}
18421848

18431849
route.window.screen.resize(new_size);
1850+
route.request_redraw();
18441851
}
18451852

18461853
WindowEvent::ScaleFactorChanged {
@@ -1853,6 +1860,7 @@ impl ApplicationHandler<EventPayload> for Application<'_> {
18531860
.screen
18541861
.set_scale(scale, route.window.winit_window.inner_size());
18551862
route.window.update_vblank_interval();
1863+
route.request_redraw();
18561864
}
18571865

18581866
WindowEvent::RedrawRequested => {

frontends/rioterm/src/context/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,14 @@ impl<T: EventListener + Clone + std::marker::Send + 'static> ContextManager<T> {
539539
);
540540
}
541541

542+
#[inline]
543+
pub fn schedule_render_on_route(&mut self, millis: u64) {
544+
self.event_proxy.send_event(
545+
RioEvent::PrepareRenderOnRoute(millis, self.current_route),
546+
self.window_id,
547+
);
548+
}
549+
542550
#[inline]
543551
pub fn report_error_fonts_not_found(&mut self, fonts_not_found: Vec<SugarloafFont>) {
544552
if !fonts_not_found.is_empty() {

frontends/rioterm/src/grid_emit.rs

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -705,28 +705,20 @@ fn ensure_drawable_sprite(
705705
)
706706
}
707707

708-
/// Emit a cursor sprite into the appropriate `fg_rows` slot. Caller
709-
/// is responsible for clearing the OTHER slot (so a previous-frame
710-
/// block doesn't linger when this frame draws a hollow, etc.) — see
711-
/// `grid.clear_cursor()`. `addCursor`
712-
///.
713-
pub fn emit_cursor_sprite(
708+
pub fn cursor_sprite_cell(
714709
grid: &mut GridRenderer,
715710
style: CursorRenderStyle,
716711
col: u16,
717712
row: u16,
718713
color: [u8; 4],
719714
cell_w: u32,
720715
cell_h: u32,
721-
) {
716+
) -> Option<(bool, CellText)> {
722717
let sprite = style.sprite();
723718
let thickness = cursor_thickness(cell_h);
724-
let Some(slot) = ensure_cursor_sprite_slot(grid, sprite, cell_w, cell_h, thickness)
725-
else {
726-
return;
727-
};
719+
let slot = ensure_cursor_sprite_slot(grid, sprite, cell_w, cell_h, thickness)?;
728720
if slot.w == 0 || slot.h == 0 {
729-
return;
721+
return None;
730722
}
731723
let cursor_cell = CellText {
732724
glyph_pos: [slot.x as u32, slot.y as u32],
@@ -742,11 +734,7 @@ pub fn emit_cursor_sprite(
742734
page: slot.page,
743735
_pad: 0,
744736
};
745-
if sprite.is_block_slot() {
746-
grid.set_block_cursor(&[cursor_cell]);
747-
} else {
748-
grid.set_non_block_cursor(&[cursor_cell]);
749-
}
737+
Some((sprite.is_block_slot(), cursor_cell))
750738
}
751739

752740
/// Underline thickness in physical pixels. fallback

frontends/rioterm/src/renderer/scrollbar.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -427,19 +427,43 @@ impl Scrollbar {
427427
.map(|(_, t)| *t)
428428
}
429429

430-
/// Returns true if any scrollbar is still animating (visible or fading).
430+
/// Returns true if a scrollbar needs per-frame redraws right now:
431+
/// while dragging or while a fade-out is in progress. Returns false
432+
/// during the fully-visible delay before the fade starts — callers
433+
/// must pair this with `next_wake_in()` to schedule the redraw that
434+
/// kicks off the fade.
431435
pub fn needs_redraw(&mut self) -> bool {
432436
if !self.enabled {
433437
return false;
434438
}
435439
if self.drag_state.is_some() {
436440
return true;
437441
}
438-
// Prune fully faded entries and check if any are still active
439442
let deadline = FADE_OUT_DELAY_MS + FADE_OUT_DURATION_MS;
440443
self.last_scroll_times
441444
.retain(|(_, t)| t.elapsed().as_millis() < deadline);
442-
!self.last_scroll_times.is_empty()
445+
self.last_scroll_times
446+
.iter()
447+
.any(|(_, t)| t.elapsed().as_millis() >= FADE_OUT_DELAY_MS)
448+
}
449+
450+
pub fn next_wake_in(&self) -> Option<std::time::Duration> {
451+
if !self.enabled || self.drag_state.is_some() {
452+
return None;
453+
}
454+
self.last_scroll_times
455+
.iter()
456+
.filter_map(|(_, t)| {
457+
let elapsed = t.elapsed().as_millis();
458+
if elapsed < FADE_OUT_DELAY_MS {
459+
Some(std::time::Duration::from_millis(
460+
(FADE_OUT_DELAY_MS - elapsed) as u64,
461+
))
462+
} else {
463+
None
464+
}
465+
})
466+
.min()
443467
}
444468
}
445469

frontends/rioterm/src/scheduler.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ impl TimerId {
2525
pub enum Topic {
2626
Render,
2727
RenderRoute,
28+
ScheduledRenderRoute,
2829
UpdateConfig,
2930
CursorBlinking,
3031
UpdateTitles,

frontends/rioterm/src/screen/mod.rs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4138,15 +4138,17 @@ impl Screen<'_> {
41384138
// `cursor.style()`):
41394139
// 1. Decide render style with strict priority:
41404140
// preedit > visible > focused > blink > shape.
4141-
// 2. Always clear both cursor slots first — last
4142-
// frame's sprite (if any) needs to disappear
4143-
// whether we emit a new one or not.
4144-
// 3. Some(style): emit a sprite into slot 0 (block)
4145-
// or slot rows+1 (others). For Block we ALSO
4146-
// write the bg-tint uniforms below so the bg
4147-
// fragment paints the block + the text shader
4148-
// inverts the underlying glyph.
4149-
// 4. None: leave both slots empty + zero uniforms.
4141+
// 2. Some(style): build the sprite for the block
4142+
// slot (drawn under text; the bg-tint uniforms
4143+
// below make the bg fragment paint the block +
4144+
// the text shader invert the underlying glyph)
4145+
// or the tail slot (bar/underline, drawn over
4146+
// text). None: both stay empty + zero uniforms.
4147+
// 3. One `grid.set_cursor(block, tail)` call
4148+
// replaces both slots. It diffs against last
4149+
// frame and only dirties cursor buffers on
4150+
// change — do NOT clear the slots beforehand,
4151+
// that would dirty them every frame.
41504152
let render_style = crate::grid_emit::cursor_render_style(
41514153
crate::grid_emit::CursorRenderInputs {
41524154
visible: p.cursor_visible,
@@ -4157,7 +4159,10 @@ impl Screen<'_> {
41574159
shape: p.cursor_shape,
41584160
},
41594161
);
4160-
grid.clear_cursor();
4162+
let mut block_cursor: Option<rio_backend::sugarloaf::grid::CellText> =
4163+
None;
4164+
let mut tail_cursor: Option<rio_backend::sugarloaf::grid::CellText> =
4165+
None;
41614166
if let Some(style) = render_style {
41624167
let cell_w = p.cell_w.round().clamp(1.0, u32::MAX as f32) as u32;
41634168
let cell_h = p.cell_h.round().clamp(1.0, u32::MAX as f32) as u32;
@@ -4167,16 +4172,23 @@ impl Screen<'_> {
41674172
(p.cursor_color[2].clamp(0.0, 1.0) * 255.0) as u8,
41684173
255,
41694174
];
4170-
crate::grid_emit::emit_cursor_sprite(
4175+
if let Some((is_block, cell)) = crate::grid_emit::cursor_sprite_cell(
41714176
grid,
41724177
style,
41734178
p.cursor_col,
41744179
p.cursor_row,
41754180
cursor_color,
41764181
cell_w,
41774182
cell_h,
4178-
);
4183+
) {
4184+
if is_block {
4185+
block_cursor = Some(cell);
4186+
} else {
4187+
tail_cursor = Some(cell);
4188+
}
4189+
}
41794190
}
4191+
grid.set_cursor(block_cursor.as_slice(), tail_cursor.as_slice());
41804192

41814193
// Panel's grid origin in drawable-pixel space =
41824194
// window scaled_margin + the panel's layout rect
@@ -4299,6 +4311,11 @@ impl Screen<'_> {
42994311
.set_dirty();
43004312
}
43014313

4314+
if let Some(wake_in) = self.renderer.scrollbar.next_wake_in() {
4315+
self.context_manager
4316+
.schedule_render_on_route(wake_in.as_millis() as u64);
4317+
}
4318+
43024319
// In case the configuration of blinking cursor is enabled
43034320
// TODO: enable blinking for selection after adding debounce (https://github.com/raphamorim/rio/issues/437)
43044321
if self.renderer.is_window_focused

rio-window/src/platform_impl/macos/display_link.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,11 @@ impl DisplayLink {
198198
}
199199
}
200200

201+
#[inline]
202+
pub fn is_running(&self) -> bool {
203+
self.is_running.get()
204+
}
205+
201206
/// Start VSync-synchronized rendering
202207
pub fn start(&self) -> Result<(), &'static str> {
203208
if self.is_running.get() {
@@ -214,9 +219,8 @@ impl DisplayLink {
214219
_ds: self.dispatch_source,
215220
});
216221

217-
// Start CVDisplayLink
218222
let result = CVDisplayLinkStart(self.display_link);
219-
if result != 0 {
223+
if result != 0 && result != -6671 {
220224
dispatch_suspend(super::dispatcher::dispatch_sys::dispatch_object_t {
221225
_ds: self.dispatch_source,
222226
});
@@ -251,10 +255,11 @@ impl DisplayLink {
251255
_ds: self.dispatch_source,
252256
});
253257

258+
self.is_running.set(false);
259+
254260
if result != 0 {
255261
Err("Failed to stop CVDisplayLink")
256262
} else {
257-
self.is_running.set(false);
258263
tracing::info!(
259264
"CVDisplayLink stopped for window {:?}",
260265
self.user_data.window_id

0 commit comments

Comments
 (0)