ui: expose the current display refresh rate from the window layer
This commit is contained in:
parent
fc70322f3d
commit
649c9e73a7
6 changed files with 74 additions and 0 deletions
|
|
@ -217,6 +217,11 @@ class Window {
|
|||
return dpi ? dpi : GetMediumDpi();
|
||||
}
|
||||
|
||||
// Last known refresh rate (Hz) of the display a window occupies, 0 while
|
||||
// unknown. Published by the common code on window open and focus gain;
|
||||
// readable from any thread (frame pacers, settings UI).
|
||||
static float CachedDisplayRefreshHz();
|
||||
|
||||
// Round trips are not guaranteed to return the same results.
|
||||
static constexpr uint32_t ConvertSizeDpi(uint32_t size, uint32_t new_dpi, uint32_t old_dpi) {
|
||||
// Always rounding up to prevent zero sizes (unless the input is zero) as
|
||||
|
|
@ -443,6 +448,14 @@ class Window {
|
|||
// medium DPI (0 returned from it will also be treated as medium DPI).
|
||||
virtual uint32_t GetLatestDpiImpl() const { return GetMediumDpi(); }
|
||||
|
||||
// Refresh rate (Hz) of the display the window currently occupies, or 0 when
|
||||
// the platform can't report it. UI thread only (native display queries);
|
||||
// consumers on other threads read the cache below.
|
||||
virtual float QueryDisplayRefreshHzImpl() const { return 0.0f; }
|
||||
// Re-query the display refresh rate and publish it to the process-wide
|
||||
// cache (UI thread; called by the common code on open and focus gain).
|
||||
void UpdateCachedDisplayRefresh();
|
||||
|
||||
// Deletion of the window may (and must) not happen in OpenImpl, the listeners
|
||||
// are deferred, so there's no need to use WindowDestructionReceiver in it.
|
||||
// In case of failure, the implementation must not leave itself in an
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ class SDLWindow final : public Window {
|
|||
|
||||
protected:
|
||||
uint32_t GetLatestDpiImpl() const override;
|
||||
float QueryDisplayRefreshHzImpl() const override;
|
||||
bool OpenImpl() override;
|
||||
void RequestCloseImpl() override;
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ class Win32Window : public Window {
|
|||
void RequestCloseImpl() override;
|
||||
|
||||
uint32_t GetLatestDpiImpl() const override;
|
||||
float QueryDisplayRefreshHzImpl() const override;
|
||||
|
||||
void ApplyNewFullscreen() override;
|
||||
void ApplyNewTitle() override;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <iterator>
|
||||
|
||||
#include <rex/assert.h>
|
||||
|
|
@ -60,6 +61,23 @@ REXCVAR_DEFINE_DOUBLE(video_mode_refresh_rate, 60.0, "GPU", "Guest video mode re
|
|||
namespace rex {
|
||||
namespace ui {
|
||||
|
||||
namespace {
|
||||
// Process-wide display refresh cache (see Window::CachedDisplayRefreshHz):
|
||||
// written on the UI thread, read from any thread (frame pacers, settings UI).
|
||||
std::atomic<float> g_display_refresh_hz{0.0f};
|
||||
} // namespace
|
||||
|
||||
float Window::CachedDisplayRefreshHz() {
|
||||
return g_display_refresh_hz.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void Window::UpdateCachedDisplayRefresh() {
|
||||
const float hz = QueryDisplayRefreshHzImpl();
|
||||
if (hz > 0.0f) {
|
||||
g_display_refresh_hz.store(hz, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
Window::Window(WindowedAppContext& app_context, const std::string_view title,
|
||||
uint32_t desired_logical_width, uint32_t desired_logical_height)
|
||||
: app_context_(app_context),
|
||||
|
|
@ -221,6 +239,7 @@ bool Window::Open() {
|
|||
return true;
|
||||
}
|
||||
phase_ = Phase::kOpen;
|
||||
UpdateCachedDisplayRefresh();
|
||||
|
||||
// Call the listeners (OnOpened with all the new state so the listeners are
|
||||
// aware that they can start interacting with the open Window, and after that,
|
||||
|
|
@ -541,6 +560,11 @@ void Window::OnFocusUpdate(bool new_has_focus, WindowDestructionReceiver& destru
|
|||
return;
|
||||
}
|
||||
has_focus_ = new_has_focus;
|
||||
if (has_focus_) {
|
||||
// Focus gain is the cheapest reliable moment to notice the window moved
|
||||
// to a display with a different refresh rate.
|
||||
UpdateCachedDisplayRefresh();
|
||||
}
|
||||
UISetupEvent e(this);
|
||||
if (has_focus_) {
|
||||
SendEventToListeners([&e](auto listener) { listener->OnGotFocus(e); }, destruction_receiver);
|
||||
|
|
|
|||
|
|
@ -406,6 +406,18 @@ void SDLWindow::HandleSDLEvent(const SDL_Event& event) {
|
|||
|
||||
uint32_t SDLWindow::GetLatestDpiImpl() const { return dpi_ ? dpi_ : kDefaultDpi; }
|
||||
|
||||
float SDLWindow::QueryDisplayRefreshHzImpl() const {
|
||||
if (!window_) {
|
||||
return 0.0f;
|
||||
}
|
||||
const SDL_DisplayID display = SDL_GetDisplayForWindow(window_);
|
||||
if (!display) {
|
||||
return 0.0f;
|
||||
}
|
||||
const SDL_DisplayMode* mode = SDL_GetCurrentDisplayMode(display);
|
||||
return mode != nullptr && mode->refresh_rate > 0.0f ? mode->refresh_rate : 0.0f;
|
||||
}
|
||||
|
||||
bool SDLWindow::OpenImpl() {
|
||||
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
|
||||
SDL_WindowFlags flags = SDL_WINDOW_RESIZABLE;
|
||||
|
|
|
|||
|
|
@ -403,6 +403,29 @@ uint32_t Win32Window::GetLatestDpiImpl() const {
|
|||
return dpi_;
|
||||
}
|
||||
|
||||
float Win32Window::QueryDisplayRefreshHzImpl() const {
|
||||
if (!hwnd_) {
|
||||
return 0.0f;
|
||||
}
|
||||
HMONITOR monitor = MonitorFromWindow(hwnd_, MONITOR_DEFAULTTONEAREST);
|
||||
if (!monitor) {
|
||||
return 0.0f;
|
||||
}
|
||||
MONITORINFOEXW mi = {};
|
||||
mi.cbSize = sizeof(mi);
|
||||
if (!GetMonitorInfoW(monitor, &mi)) {
|
||||
return 0.0f;
|
||||
}
|
||||
DEVMODEW dm = {};
|
||||
dm.dmSize = sizeof(dm);
|
||||
if (!EnumDisplaySettingsW(mi.szDevice, ENUM_CURRENT_SETTINGS, &dm) ||
|
||||
!(dm.dmFields & DM_DISPLAYFREQUENCY) || dm.dmDisplayFrequency <= 1) {
|
||||
// 0 and 1 are the documented "hardware default" sentinels.
|
||||
return 0.0f;
|
||||
}
|
||||
return float(dm.dmDisplayFrequency);
|
||||
}
|
||||
|
||||
void Win32Window::ApplyNewFullscreen() {
|
||||
// Various functions here may send messages that may result in the
|
||||
// listeners being invoked, and potentially cause the destruction of the
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue