Skip to content

backend: change VRR behavior - #2420

Draft
skygrango wants to merge 3 commits into
pop-os:masterfrom
skygrango:modify-vrr-behavior
Draft

backend: change VRR behavior#2420
skygrango wants to merge 3 commits into
pop-os:masterfrom
skygrango:modify-vrr-behavior

Conversation

@skygrango

@skygrango skygrango commented May 27, 2026

Copy link
Copy Markdown
Contributor

Introducing changes:

  • VRR is no longer enabled just because a fullscreen window exists; keyboard focus is now also required when cursor on the same display.
  • Correctly use the minimum VRR value.
  • Allow exclusive fullscreen surface to drive display updates and render its updates immediately.

Previously, VRR stayed enabled when a fullscreen window existed in the background, even after the user switched focus to another window, which led to unexpected visual flickering.

Under VRR, the current rendering scheduler relies on predictive timing to determine when to render the next frame. This works well when coordinating updates from multiple surfaces. However, an exclusive fullscreen surface represents a different scheduling model, as its presentation cadence can be driven directly.

An exclusive fullscreen surface has its own presentation cadence. Scheduling decisions based solely on prediction allow updates from unrelated background surfaces to influence the compositor's render timing. As a result, the compositor may miss the optimal rendering window for the fullscreen surface, disrupting its presentation cadence. This may result in additional VRR flickering, especially when VSync is enabled, so this can provide better results once fifo-v1 is supported.

To avoid these competing sources of scheduling interference, the exclusive fullscreen surface should become the primary driver of rendering while VRR is active. This not only reduces latency and avoids missing presentation opportunities, but also provides a cleaner foundation for further improvements to the predictive scheduling algorithm. Restricting this behavior to exclusive fullscreen surfaces keeps the existing scheduling behavior unchanged for all other workloads, minimizing the risk of regressions while providing a dedicated path for improving the predictive scheduling algorithm for fullscreen rendering.

I provided two env to rollback behavior:

  • COSMIC_FULLSCREEN_IMMEDIATE_RENDER: if false, disable immediate draw for fullscreen surface

  • COSMIC_FULLSCREEN_SKIP_OTHER_SURFACE: if false, keep redraw requests from background surfaces

  • I have disclosed use of any AI generated code in my commit messages.

    • If you are using an LLM, and do not fully understand the changes it is making to the code base, do not create a PR.
    • In our experience, AI generated code often results in overly complex code that lacks enough context for a proper fix or feature inclusion. This results in considerably longer code reviews. Due to this, AI authored or partially authored PRs may be closed without comment.
  • I understand these changes in full and will be able to respond to review comments.

  • My change is accurately described in the commit message.

  • My contribution is tested and working as described.

  • I have read the Developer Certificate of Origin and certify my contribution under its conditions.

.unwrap_or(min_min_refresh_interval) // alternatively use 30Hz
.max(min_min_refresh_interval),
));
.min(min_min_refresh_interval),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem correct. This would cause the 30hz duration to be used, even if the display reports e.g. 40Hz as a minimum, triggering low-framerate compensation.

The current code ensures we never drop below 30hz, which is by design.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my monitor min vrr rate is 48, so it should be 1. / 48.,1/48 is smaller than 1/30, which does indeed fix my problem. theoretically, we should try to stay within the VRR range, right?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh right, this is a duration. 🤦‍♀️ yeah makes sense. higher hz values would have smaller frame times. thanks

.unwrap_or(min_min_refresh_interval) // alternatively use 30Hz
.max(min_min_refresh_interval),
));
.min(min_min_refresh_interval),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh right, this is a duration. 🤦‍♀️ yeah makes sense. higher hz values would have smaller frame times. thanks

.unwrap_or(Duration::from_nanos(1_000_000_000 / 30));
let drives_refresh_rate = fullscreen_surface.wl_surface().is_some_and(|surface| {
recursive_frame_time_estimation(&self.clock, &surface)
.is_some_and(|dur| dur <= min_vrr_frame_time)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I am not completely sure we want to use the min-frame-time here though.

This check just serves to figure out, if the application roughly gives us a constantly updating screen, so that we don't turn on VRR e.g. for a fullscreen browser.

If we enforce the check to be not lower than the minimum refresh rate, we might disable VRR if the game dips below the e.g. 48 hz for a second before recovering.

Do you still see flickering, if this is not using min_vrr_frame_time?

@skygrango skygrango Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on my understanding, temporarily disabling VRR is not a desirable solution. My OLED display suffers from noticeable brightness flicker when there are large fluctuations in refresh rate, and as far as I know, this is an inherent hardware limitation of current OLED panels.

The brightness flickering issue is quite complex. While working on the commit-timing-v1 and fifo-v1 protocols, I found that even with both protocols implemented, brightness flicker can still occur due to jitter originating from the game engine itself. This is because the current next_presentation_time and next_render_time calculations rely on previous frame times to determine an appropriate refresh rate. If the game's frame output is unstable, the display may quickly ramp up to its maximum refresh rate in response to short frame times, persistent jitter can cause continuous refresh rate fluctuations and therefore visible brightness flicker.

I can consistently reproduce these issues in Elden Ring and Sekiro™: Shadows Die Twice. I am experimenting with improvements to the scheduling algorithm. I already have a potential solution, but I'm not sure whether this is a feature the System76 team would want to adopt. The idea is to allow users to specify a target refresh rate whenever fullscreen VRR is enabled. This value could be adjusted at any time through the settings. In practice, this can almost completely eliminate brightness flickering caused by large refresh rate fluctuations while still preserving the benefits of VRR. It could also serve as an FPS limiter, similar to how Gamescope operates. By keeping the display refresh rate close to a user-defined target instead of constantly chasing short-term frame time variations, the display remains much more stable and flicker is significantly reduced(not found yet).

image

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would this target rate be implemented?
How does it compare to in-game or DXVK/VKD3D level frame limiting (latency/stability tradeoff)? AFAIR Gamescope's frame limiting has higher latency than DXVK/VKD3D level, which also tends to be higher than a proper in-engine limiter.

On my hardware er-patcher frame limiting for Elden Ring and dxvk-low-latency for DXVK helps quite a bit in terms of flickering.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My implementation requires using it together with fifo-v1. I did not introduce any additional latency in between, and theoretically, providing refresh rate control at the compositor level should achieve better results. At least in my tests, it performs better than MangoHud.

My implementation is based on the existing infrastructure of cosmic-comp, with adjustments made to the prediction method. It calculates the time from rendering to vblank present for previous frames and uses that as statistical data to accurately estimate how long before the present time the compositor should start drawing and submitting the frame.

In my solution, the evaluation is based on the target refresh rate configured by the user. For example, if the game is actually rendering at 120Hz (8.3ms) but the user requests 60Hz (16.6ms), the compositor will delay starting the rendering and submission of the current frame by 8.3ms. This effectively makes the actual refresh rate become 60Hz. additionally, through fifo-v1, the game will also wait for the compositor to signal that it is ready before submitting the next frame.

If the prediction detects that the frame display time is longer than the target 60Hz (16.6ms) interval, the compositor will immediately render without waiting. In this case, the GPU can be utilized to the maximum extent, and there is no additional latency introduced.

The main goal is to eliminate excessive chasing of dynamically changing frame rates in VRR scenarios. This can provide a smoother gaming experience while significantly reducing the occurrence of flickering.

At the same time, It also provides a convenient way to adjust the target refresh rate at any time according to the requirements of the game scene. Users can fine-tune the desired balance between frame stability and smoothness depending on the game itself or even different situations within the same game.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my understanding, the approaches you mentioned, such as DXVK/VKD3D/MangoHud-level frame limiting, all intervene in the swap chain to achieve the goal. However, these methods cannot truly know when the compositor will present the frame to the display; they can only make indirect estimations. This introduces latency and instability, although they are still effective solutions.

gamescope is somewhat different. It has a dedicated vblank manager to strictly control the presentation rate of frames. From an implementation perspective, it is closer to my approach. However, gamescope is based on a time-driven frame update model, while my current implementation still relies on the surface submission-driven presentation model provided by cosmic-comp.

Because of this, gamescope can basically prevent refresh-rate spikes completely, which helps avoid flickering issues on OLED displays. The trade-off is that when the display refresh rate and the game's submission rate are not aligned, additional latency can be introduced.

My approach, on the other hand, schedules rendering and presentation immediately after the game submits an update. Therefore, in theory, it can provide lower latency.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  Make: Dell Inc.
  Model: DELL S3422DWG
  Physical Size: 800 x 330 mm
  Position: 0,0
  Scale: 100%
  Transform: normal
  Adaptive Sync Support: true
  Adaptive Sync: automatic
  Xwayland primary: true
  VRR Target Rate: 119.000 Hz

I tested your vrr implementation on this monitor on AC Black Flag Resynced and it definitely reduced the flickers a lot.

It was a very smooth experience. Much smoother than on GNOME for sure, haven't tried KDE.

I used it with Mangohud's frame limit set to 118 fps and early. For the smoothest experience what do you suggest?

The game was hovering from 90-100fps and my monitor's refresh rate is set to 120hz.

Thank you

@skygrango skygrango Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I packaged your vrr patches for the AUR

Oh, I didn't realize you are using an Arch-based os; I have a similar AUR package too—haha: cosmic-comp-gaming. It's great to set up your own AUR package for management.

For cosmic comp I also add my PR #2673

I’ve included a similar fix for the VRR configuration, but your approach might be better—please keep up the good work.

I used it with Mangohud's frame limit set to 118 fps and early. For the smoothest experience what do you suggest?
The game was hovering from 90-100fps and my monitor's refresh rate is set to 120hz.

  • If game's frame timing is good, then mangohud's limiter is not needed.
  • If game supports a VSync toggle, try enabling it; some games may achieve better frame timing as a result.
  • If game has a built-in frame limiter, you might want to try using it; since it may incorporate a specialized frame generation mechanism, the game's native limiter could yield better results.
  • If certain games (such as Elden Ring) have a preset frame rate cap 60 with Vsync, you can set the VRR Target Rate to 60 as well.
  • If you are playing videos in a browser—especially Firefox, which has VSync issues—it is recommended to set the VRR Target Rate to 60.

Setting the VRR Target Rate to the maximum value means displaying frames as quickly as possible; this setting aims to minimize input lag. However, frame timing performance is likely to be poor, as GPU utilization is maxed out, leaving no headroom to maintain stable frame timings.

If the game's frame rate range is 90–100, I personally prefer setting VRR Target Rate to 80–90; this results in more stable frame timing, especially in 3D games with significant scene changes.

@skygrango skygrango Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also like to clarify that this PR only covers VRR management for fullscreen mode and an experimental immediate rendering feature for fullscreen mode. It does not include the VRR Target Rate functionality.

The Git branch specified in the script I provided includes everything from this PR, but also adds support for fifo-v1, the VRR Target Rate feature, and several other experimental changes that may improve performance.

don't forget to run command: sudo setcap cap_sys_nice=eip /usr/bin/cosmic-comp
This helps with performance. I added the thread priority feature.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@skygrango I found leaving VRR Target Rate at 118 to be more smoother, using the game's fps limiter is also looking smoother.

Overall the patch from your other branch has helped a lot with the flicker and overall smoothness. Thank you for your work!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AdityaHebballe Thank you very much for helping with the testing. I'm glad that it is helpful.

I believe that because everyone has different hardware performance, displays, game content, and sensitivity to VRR behavior, the ideal VRR strategy may vary from person to person. Therefore, I took inspiration from gamescope's approach of allowing users to specify the VRR rate, so that players can customize their gaming experience.

If you have any further feedback or testing results, please move the discussion to VRR Target Rate Report. This will help keep this PR focused on technical discussions.

@IlChitarrista

Copy link
Copy Markdown

VRR is no longer enabled just because a fullscreen window exists; keyboard focus is now also required.

There are many cases where this isn't necessarily a good idea.
For gaming use cases, assuming mouse and keyboard, it's guaranteed that the game window will be focused while gaming, but it's not necessarily the case for gamepad input.
Another important use case for VRR is video playback which also doesn't necessarily require the window to be focused.

This would reduce flickering for mostly static non-VRR content (eg. fullscreen web-browser on a secondary monitor statically updating) but doing so non-transparently sounds like it could break people's usage of VRR accidentally.

@skygrango

Copy link
Copy Markdown
Contributor Author

There are many cases where this isn't necessarily a good idea. For gaming use cases, assuming mouse and keyboard, it's guaranteed that the game window will be focused while gaming, but it's not necessarily the case for gamepad input. Another important use case for VRR is video playback which also doesn't necessarily require the window to be focused.

Using only gamepad may not meet the usage requirements of Cosmic DE

This would reduce flickering for mostly static non-VRR content (eg. fullscreen web-browser on a secondary monitor statically updating) but doing so non-transparently sounds like it could break people's usage of VRR accidentally.

I agree. The option I'm considering is that if the cursor isn't on the screen, then keyboard focus is no longer needed.

@IlChitarrista

Copy link
Copy Markdown

Using only gamepad may not meet the usage requirements of Cosmic DE

For overall use it doesn't, for gaming specifically it's common and should be properly supported; We shouldn't non-transparently break VRR just because the game window doesn't have to be focused for gamepad input to work.

I agree. The option I'm considering is that if the cursor isn't on the screen, then keyboard focus is no longer needed.

If the cursor isn't on the screen and we're dealing with a fullscreen window isn't it already guaranteed to have keyboard focus since it would be the only visible window anyways?

@skygrango

Copy link
Copy Markdown
Contributor Author

For overall use it doesn't, for gaming specifically it's common and should be properly supported; We shouldn't non-transparently break VRR just because the game window doesn't have to be focused for gamepad input to work.

As far as I know, as long as a mouse button press event is sent to the window, the window will gain keyboard focus. I'm not yet sure how well it's supported in Cosmic DE when only a gamepad is used.

If the cursor isn't on the screen and we're dealing with a fullscreen window isn't it already guaranteed to have keyboard focus since it would be the only visible window anyways?

This condition is not suitable for handling in KMS; it should be addressed when the window status changes. I need to study the current implementation more deeply.

@skygrango

Copy link
Copy Markdown
Contributor Author

I added FullscreenOccupied and store the result in the Output structure to avoid requiring the KMS surface thread to recalculate it every time. update the value of FullscreenOccupied in update_active, this means we only update when the window's state actually changes.

This time, I use the focus_stack owned by each Workspace to check whether it is the last focused element. This allows VRR to continue functioning properly even when there is no active seat on the output.

At the same time, I also ensure that on Output with an active seat, VRR still requires keyboard focus before being enabled. This is because the current focus_stack does not record anything other than Element or Fullscreen, so opening the COSMIC App Library with the Super key does not update this stack. To ensure the behavior remains as expected, I still keep keyboard focus as a required condition.

@skygrango
skygrango force-pushed the modify-vrr-behavior branch from 76b926a to cff68f9 Compare June 23, 2026 19:54
@skygrango

Copy link
Copy Markdown
Contributor Author

I need to set up a more suitable environment for testing. I believe the implementation still has issues and needs adjustment, so I'll change this to a draft first.

@skygrango
skygrango marked this pull request as draft June 28, 2026 00:30
@skygrango
skygrango force-pushed the modify-vrr-behavior branch from cff68f9 to ba2deb3 Compare July 31, 2026 04:06
@skygrango

Copy link
Copy Markdown
Contributor Author

I allow an exclusive fullscreen surface to supersede any pending redraw requests already queued by other surfaces.

This should address the frame pacing issues reported in #2675 by minimizing interference from surface updates originating from other windows while an exclusive fullscreen surface is active.

At the same time, if the exclusive fullscreen surface becomes unresponsive or stops producing updates, redraw requests from other surfaces will continue to be processed normally instead of being blocked indefinitely.

@skygrango

Copy link
Copy Markdown
Contributor Author

After taking a closer look at the results, I realized that cosmic-comp schedules rendering based on previous presentation timing. Because of that, replacing existing redraw requests doesn't actually provide any benefit.

revert this change.

@skygrango
skygrango force-pushed the modify-vrr-behavior branch from ba2deb3 to 9a2b14e Compare July 31, 2026 04:59
@skygrango

Copy link
Copy Markdown
Contributor Author

After investigating this further, I believe it is beneficial to render an exclusive fullscreen surface immediately after it commit surface. This should help reduce latency by avoiding unnecessary waiting, so I've cleaned up the implementation and resubmitted the commit.

ensure that VRR is enabled only when the fullscreen window occupy the
output and use the minimum VRR value correctly
allow replacing the redraw request in the current redraw queue
and Ignore update requests from non-fullscreen surfaces.

Fallback environment variable:
introduce COSMIC_FULLSCREEN_IMMEDIATE_RENDER for disable immdediately render fullscreen surface
introduce COSMIC_FULLSCREEN_SKIP_OTHER_SURFACE to skip non-fullscreen surfaces
@skygrango
skygrango force-pushed the modify-vrr-behavior branch from f8e4206 to 7cd2f95 Compare August 21, 2026 11:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants