Attempt to make cursor updates more efficient by queueing and performing once per frame, skip repaint trigger if a cursor update occurs

This commit is contained in:
wheaney 2024-07-03 12:53:06 -07:00
parent 707710c4c7
commit 5192815098
3 changed files with 66 additions and 40 deletions

View File

@ -11,8 +11,6 @@ export class CursorManager {
this._mainActor = mainActor;
this._refreshRate = refreshRate;
this._changeHookFn = null;
// Set/destroyed by _enableCloningMouse/_disableCloningMouse
this._cursorWantedVisible = null;
this._cursorTracker = null;
@ -131,8 +129,8 @@ export class CursorManager {
} else {
this._mainActor.add_actor(this._cursorActor);
}
this._cursorChangedConnection = this._cursorTracker.connect('cursor-changed', this._updateMouseSprite.bind(this));
this._cursorVisibilityChangedConnection = this._cursorTracker.connect('visibility-changed', this._handleVisibilityChanged.bind(this));
this._cursorChangedConnection = this._cursorTracker.connect('cursor-changed', this._queueSpriteUpdate.bind(this));
this._cursorVisibilityChangedConnection = this._cursorTracker.connect('visibility-changed', this._queueVisibilityUpdate.bind(this));
// Some elements will occasionally appear above the cursor, so we periodically reset the actor stacking.
// This could theoretically be fixed "better" by attaching to all events that might affect actor ordering,
@ -144,11 +142,11 @@ export class CursorManager {
}).bind(this));
const interval = 1000 / this._refreshRate;
this._cursorWatch = this._cursorWatcher.addWatch(interval, this._updateMousePosition.bind(this));
this._cursorWatch = this._cursorWatcher.addWatch(interval, this._queuePositionUpdate.bind(this));
const [x, y] = global.get_pointer();
this._updateMousePosition(x, y);
this._updateMouseSprite();
this._queuePositionUpdate(x, y);
this._queueSpriteUpdate();
}
if (this._cursorTracker.set_keep_focus_while_hidden) {
@ -203,34 +201,58 @@ export class CursorManager {
}
}
_updateMousePosition(x, y) {
this._cursorActor.set_position(x, y);
_queuePositionUpdate(x, y) {
this._queued_cursor_position = [x, y];
}
_updateMouseSprite() {
const sprite = this._cursorTracker.get_sprite();
if (sprite) {
this._cursorSprite.content.texture = sprite;
this._cursorSprite.show();
} else {
this._cursorSprite.hide();
_queueSpriteUpdate() {
this._queued_sprite_update = true;
}
_queueVisibilityUpdate() {
this._cursorTrackerSetPointerVisibleBound(false);
this._queued_visibility_update = true;
this._queueSpriteUpdate();
}
handleNewFrame() {
let redraw = false;
if (this._queued_cursor_position) {
const [x, y] = this._queued_cursor_position;
this._cursorActor.set_position(x, y);
this._queued_cursor_position = null;
redraw = true;
}
const [xHot, yHot] = this._cursorTracker.get_hot();
this._cursorSprite.set({
translation_x: -xHot,
translation_y: -yHot,
});
}
if (this._queued_sprite_update) {
const sprite = this._cursorTracker.get_sprite();
if (sprite) {
this._cursorSprite.content.texture = sprite;
this._cursorSprite.show();
} else {
this._cursorSprite.hide();
}
const [xHot, yHot] = this._cursorTracker.get_hot();
this._cursorSprite.set({
translation_x: -xHot,
translation_y: -yHot,
});
this._queued_sprite_update = false;
redraw = true;
}
_handleVisibilityChanged() {
this._cursorTrackerSetPointerVisibleBound(false);
this._updateMouseSprite();
if (this._queued_visibility_update) {
this._queued_visibility_update = false;
redraw = true;
}
return redraw;
}
// updates the stacking and other attributes that are hard to track and may periodically get out of sync
_periodicReset() {
this._handleVisibilityChanged();
this._queueVisibilityUpdate();
this._mainActor.set_child_above_sibling(this._cursorActor, null);
// some other processes are uninhibiting when they shouldn't, so we need to re-inhibit here

View File

@ -52,6 +52,7 @@ export default class BreezyDesktopExtension extends Extension {
this._disable_anti_aliasing_binding = null;
this._optimal_monitor_config_binding = null;
this._headset_as_primary_binding = null;
this._redraw_timeline = null;
if (!Globals.logger) {
Globals.logger = new Logger({
@ -207,7 +208,7 @@ export default class BreezyDesktopExtension extends Extension {
try {
const targetMonitor = this._target_monitor.monitor;
const refreshRate = targetMonitor.refreshRate ?? 60;
this._cursor_manager = new CursorManager(Main.layoutManager.uiGroup, refreshRate * 1.05);
this._cursor_manager = new CursorManager(Main.layoutManager.uiGroup, refreshRate);
this._cursor_manager.enable();
this._overlay = new St.Bin({ style: 'background-color: rgba(0, 0, 0, 1);'});
@ -263,6 +264,17 @@ export default class BreezyDesktopExtension extends Extension {
this._add_settings_keybinding('recenter-display-shortcut', this._recenter_display.bind(this));
this._add_settings_keybinding('toggle-display-distance-shortcut', this._xr_effect._change_distance.bind(this._xr_effect));
this._add_settings_keybinding('toggle-follow-shortcut', this._toggle_follow_mode.bind(this));
this._redraw_timeline = Clutter.Timeline.new_for_actor(Main.layoutManager.uiGroup, 1000);
this._redraw_timeline.connect('new-frame', (() => {
if (this._is_effect_running) {
// if the cursor's frame handler triggered a redraw, we'll skip triggering it
const skip_repaint = this._cursor_manager?.handleNewFrame();
if (!skip_repaint) this._xr_effect?.queue_repaint();
}
}).bind(this));
this._redraw_timeline.set_repeat_count(-1);
this._redraw_timeline.start();
} catch (e) {
Globals.logger.log(`ERROR: BreezyDesktopExtension _effect_enable ${e.message}\n${e.stack}`);
this._effect_disable();
@ -372,6 +384,11 @@ export default class BreezyDesktopExtension extends Extension {
Globals.logger.log_debug('BreezyDesktopExtension _effect_disable');
this._is_effect_running = false;
if (this._redraw_timeline) {
this._redraw_timeline.stop();
this._redraw_timeline = null;
}
if (this._running_poller_id) {
GLib.source_remove(this._running_poller_id);
this._running_poller_id = undefined;

View File

@ -292,8 +292,6 @@ export const XREffect = GObject.registerClass({
this.customBannerImage = new Clutter.Image();
this.customBannerImage.set_data(customBanner.get_pixels(), Cogl.PixelFormat.RGB_888,
customBanner.width, customBanner.height, customBanner.rowstride);
this._redraw_timeline = null;
}
_change_distance() {
@ -341,13 +339,6 @@ export const XREffect = GObject.registerClass({
this.setIntermittentUniformVariables = setIntermittentUniformVariables.bind(this);
this.setIntermittentUniformVariables();
this._redraw_timeline = Clutter.Timeline.new_for_actor(this.get_actor(), 1000);
this._redraw_timeline.connect('new-frame', (() => {
this.queue_repaint();
}).bind(this));
this._redraw_timeline.set_repeat_count(-1);
this._redraw_timeline.start();
this._uniforms_timeout_id = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 250, (() => {
this.setIntermittentUniformVariables();
return GLib.SOURCE_CONTINUE;
@ -394,10 +385,6 @@ export const XREffect = GObject.registerClass({
}
cleanup() {
if (this._redraw_timeline) {
this._redraw_timeline.stop();
this._redraw_timeline = null;
}
if (this._uniforms_timeout_id) GLib.source_remove(this._uniforms_timeout_id);
}
});