Skip to content

Win32 - Support rendering at max refresh rate of current displays - #21643

Merged
MrJul merged 2 commits into
masterfrom
win_32_max_refresh_rate
Jun 30, 2026
Merged

Win32 - Support rendering at max refresh rate of current displays#21643
MrJul merged 2 commits into
masterfrom
win_32_max_refresh_rate

Conversation

@emmauss

@emmauss emmauss commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

What does the pull request do?

Updates SleepRenderTimer with the max refresh rate reported for the displays available on Windows. This allows Vulkan and WGL to render at the highest refresh rate available, though there are some inconsistencies due to the lack of hardware timing.
This doesn't affect AngleGL because that backend uses the DWM Composition as timer.

WGL and Vulkan behave differently based on display and driver settings.
WGL: On a Nvidia GPU

  • Using default driver settings, render rate will be locked to the lowest refresh rate currently being used among all displays. 2 monitors rendering at 60hz and 120hz will result in Avalonia rendering at 60fps on both monitors.
  • Setting VSync driver setting to Fast, render rate will be locked to the refresh rate of the last display the window was resized on. 2 monitors with 60hz and 120hz will result in Avalonia rendering at 120fps if initially moved from the second monitor to the first, and 60fps if initially moved from the first monitor to the second. Resizing will reset the frame rate to the current monitor.

Vulkan:

  • We use MAILBOX present mode by default, which allows render as fast as we can without tearing, though the driver decides how fast we render. In this mode, Avalonia renders at the highest frame rate available when the window is first created and shown, and will reset to the current display's refresh rate when resized. It will then onwards adapt to the display it's moved to. This behavior was only observed when the window is resized on a slower refresh rate display and moved to a higher one and back. When resized on a higher refresh rate display, it will favor the high frame rate even when moved to a slower one, until it's resized again.
  • When FIFO present mode in forced internally, It will lock to the lowest refresh rate of the displays available when driver settings are default. If VSync is set to Fast or equivalent, it will adapt to the current display's refresh rate, dynamically switching with no need to resize. This would have been the more favored present mode, if it didn't rely on the driver settings.

What is the current behavior?

Rendering is locked to 60fps, regardless of the display's refresh rate.

What is the updated/expected behavior with this PR?

How was the solution implemented (if it's not obvious)?

Checklist

Breaking changes

Obsoletions / Deprecations

Fixed issues

Comment on lines +136 to +145

var maxDisplayFrequency = Instance.Screen?.AllScreens?.Max(s => (s as WinScreen)?.Frequency);

if (maxDisplayFrequency != null &&
maxDisplayFrequency != 0 &&
renderTimer is SleepLoopRenderTimer sleepLoopRenderTimer)
{
sleepLoopRenderTimer.DesiredFps = (int)maxDisplayFrequency;
}

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.

Please extract this logic and reuse it here and in WindowImpl.

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.

Done.

AvaloniaLocator.CurrentMutable.Bind<IPlatformDragSource>().ToSingleton<DragSource>();


var maxDisplayFrequency = Instance.Screen?.AllScreens?.Max(s => (s as WinScreen)?.Frequency);

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.

In #18558 for X11, we take the larger of 60 and the maximum refresh rate. Even though sub-60Hz monitors are pretty rare, please do the same here so we're consistent and always ensure at least 60 FPS.

Note: we can tweak this behavior later or expose a configuration knob to the users if needed.

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.

Done

@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 12.1.999-cibuild0066900-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 12.1.999-cibuild0066928-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@MrJul MrJul left a comment

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.

LGTM!

@MrJul
MrJul added this pull request to the merge queue Jun 30, 2026
Merged via the queue into master with commit 4bd9909 Jun 30, 2026
11 checks passed
@MrJul
MrJul deleted the win_32_max_refresh_rate branch June 30, 2026 08:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants