mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-20 15:25:38 +00:00
WindowInfo: Get refresh rate from monitor config
DwmGetCompositionTimingInfo() returns a noisy refresh rate, at least on Win11 22H2.
This commit is contained in:
parent
f9b58c4077
commit
d9cc80c7f9
|
@ -2845,12 +2845,11 @@ void System::UpdateSpeedLimiterState()
|
||||||
if (g_settings.sync_to_host_refresh_rate &&
|
if (g_settings.sync_to_host_refresh_rate &&
|
||||||
(g_settings.audio_stream_parameters.stretch_mode != AudioStretchMode::Off) && s_target_speed == 1.0f && IsValid())
|
(g_settings.audio_stream_parameters.stretch_mode != AudioStretchMode::Off) && s_target_speed == 1.0f && IsValid())
|
||||||
{
|
{
|
||||||
float host_refresh_rate;
|
if (std::optional<float> host_refresh_rate = g_gpu_device->GetHostRefreshRate(); host_refresh_rate.has_value())
|
||||||
if (g_gpu_device->GetHostRefreshRate(&host_refresh_rate))
|
|
||||||
{
|
{
|
||||||
const float ratio = host_refresh_rate / System::GetThrottleFrequency();
|
const float ratio = host_refresh_rate.value() / System::GetThrottleFrequency();
|
||||||
s_syncing_to_host = (ratio >= 0.95f && ratio <= 1.05f);
|
s_syncing_to_host = (ratio >= 0.95f && ratio <= 1.05f);
|
||||||
Log_InfoPrintf("Refresh rate: Host=%fhz Guest=%fhz Ratio=%f - %s", host_refresh_rate,
|
Log_InfoPrintf("Refresh rate: Host=%fhz Guest=%fhz Ratio=%f - %s", host_refresh_rate.value(),
|
||||||
System::GetThrottleFrequency(), ratio, s_syncing_to_host ? "can sync" : "can't sync");
|
System::GetThrottleFrequency(), ratio, s_syncing_to_host ? "can sync" : "can't sync");
|
||||||
if (s_syncing_to_host)
|
if (s_syncing_to_host)
|
||||||
s_target_speed *= ratio;
|
s_target_speed *= ratio;
|
||||||
|
|
|
@ -585,7 +585,7 @@ void D3D11Device::InvalidateRenderTarget(GPUTexture* t)
|
||||||
static_cast<D3D11Texture*>(t)->CommitClear(m_context.Get());
|
static_cast<D3D11Texture*>(t)->CommitClear(m_context.Get());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D11Device::GetHostRefreshRate(float* refresh_rate)
|
std::optional<float> D3D11Device::GetHostRefreshRate()
|
||||||
{
|
{
|
||||||
if (m_swap_chain && m_is_exclusive_fullscreen)
|
if (m_swap_chain && m_is_exclusive_fullscreen)
|
||||||
{
|
{
|
||||||
|
@ -595,13 +595,12 @@ bool D3D11Device::GetHostRefreshRate(float* refresh_rate)
|
||||||
{
|
{
|
||||||
Log_InfoPrintf("using fs rr: %u %u", desc.BufferDesc.RefreshRate.Numerator,
|
Log_InfoPrintf("using fs rr: %u %u", desc.BufferDesc.RefreshRate.Numerator,
|
||||||
desc.BufferDesc.RefreshRate.Denominator);
|
desc.BufferDesc.RefreshRate.Denominator);
|
||||||
*refresh_rate = static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
|
return static_cast<float>(desc.BufferDesc.RefreshRate.Numerator) /
|
||||||
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
static_cast<float>(desc.BufferDesc.RefreshRate.Denominator);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return GPUDevice::GetHostRefreshRate(refresh_rate);
|
return GPUDevice::GetHostRefreshRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool D3D11Device::BeginPresent(bool skip_present)
|
bool D3D11Device::BeginPresent(bool skip_present)
|
||||||
|
|
|
@ -96,7 +96,7 @@ public:
|
||||||
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
||||||
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
||||||
|
|
||||||
bool GetHostRefreshRate(float* refresh_rate) override;
|
std::optional<float> GetHostRefreshRate() override;
|
||||||
|
|
||||||
bool SetGPUTimingEnabled(bool enabled) override;
|
bool SetGPUTimingEnabled(bool enabled) override;
|
||||||
float GetAndResetAccumulatedGPUTime() override;
|
float GetAndResetAccumulatedGPUTime() override;
|
||||||
|
|
|
@ -1052,15 +1052,12 @@ void GPUDevice::ThrottlePresentation()
|
||||||
Common::Timer::SleepUntil(m_last_frame_displayed_time, false);
|
Common::Timer::SleepUntil(m_last_frame_displayed_time, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPUDevice::GetHostRefreshRate(float* refresh_rate)
|
std::optional<float> GPUDevice::GetHostRefreshRate()
|
||||||
{
|
{
|
||||||
if (m_window_info.surface_refresh_rate > 0.0f)
|
if (m_window_info.surface_refresh_rate > 0.0f)
|
||||||
{
|
return m_window_info.surface_refresh_rate;
|
||||||
*refresh_rate = m_window_info.surface_refresh_rate;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return WindowInfo::QueryRefreshRateForWindow(m_window_info, refresh_rate);
|
return WindowInfo::QueryRefreshRateForWindow(m_window_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPUDevice::SetGPUTimingEnabled(bool enabled)
|
bool GPUDevice::SetGPUTimingEnabled(bool enabled)
|
||||||
|
|
|
@ -689,7 +689,7 @@ public:
|
||||||
|
|
||||||
virtual bool SupportsTextureFormat(GPUTexture::Format format) const = 0;
|
virtual bool SupportsTextureFormat(GPUTexture::Format format) const = 0;
|
||||||
|
|
||||||
virtual bool GetHostRefreshRate(float* refresh_rate);
|
virtual std::optional<float> GetHostRefreshRate();
|
||||||
|
|
||||||
/// Enables/disables GPU frame timing.
|
/// Enables/disables GPU frame timing.
|
||||||
virtual bool SetGPUTimingEnabled(bool enabled);
|
virtual bool SetGPUTimingEnabled(bool enabled);
|
||||||
|
|
|
@ -260,7 +260,7 @@ public:
|
||||||
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
||||||
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
||||||
|
|
||||||
bool GetHostRefreshRate(float* refresh_rate) override;
|
std::optional<float> GetHostRefreshRate() override;
|
||||||
|
|
||||||
bool SetGPUTimingEnabled(bool enabled) override;
|
bool SetGPUTimingEnabled(bool enabled) override;
|
||||||
float GetAndResetAccumulatedGPUTime() override;
|
float GetAndResetAccumulatedGPUTime() override;
|
||||||
|
|
|
@ -119,9 +119,9 @@ bool MetalDevice::HasSurface() const
|
||||||
return (m_layer != nil);
|
return (m_layer != nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MetalDevice::GetHostRefreshRate(float* refresh_rate)
|
std::optional<float> MetalDevice::GetHostRefreshRate()
|
||||||
{
|
{
|
||||||
return GPUDevice::GetHostRefreshRate(refresh_rate);
|
return GPUDevice::GetHostRefreshRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetalDevice::SetVSyncEnabled(bool enabled)
|
void MetalDevice::SetVSyncEnabled(bool enabled)
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#include "window_info.h"
|
#include "window_info.h"
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/error.h"
|
||||||
|
#include "common/heap_array.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
#include "common/scoped_guard.h"
|
||||||
|
|
||||||
|
Log_SetChannel(WindowInfo);
|
||||||
|
|
||||||
void WindowInfo::SetSurfaceless()
|
void WindowInfo::SetSurfaceless()
|
||||||
{
|
{
|
||||||
|
@ -25,11 +31,80 @@ void WindowInfo::SetSurfaceless()
|
||||||
#include "common/windows_headers.h"
|
#include "common/windows_headers.h"
|
||||||
#include <dwmapi.h>
|
#include <dwmapi.h>
|
||||||
|
|
||||||
static bool GetRefreshRateFromDWM(HWND hwnd, float* refresh_rate)
|
static std::optional<float> GetRefreshRateFromDisplayConfig(HWND hwnd)
|
||||||
|
{
|
||||||
|
// Partially based on Chromium ui/display/win/display_config_helper.cc.
|
||||||
|
const HMONITOR monitor = MonitorFromWindow(hwnd, 0);
|
||||||
|
if (!monitor) [[unlikely]]
|
||||||
|
{
|
||||||
|
Log_ErrorFmt("{}() failed: {}", "MonitorFromWindow", Error::CreateWin32(GetLastError()).GetDescription());
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
MONITORINFOEXW mi = {};
|
||||||
|
mi.cbSize = sizeof(mi);
|
||||||
|
if (!GetMonitorInfoW(monitor, &mi))
|
||||||
|
{
|
||||||
|
Log_ErrorFmt("{}() failed: {}", "GetMonitorInfoW", Error::CreateWin32(GetLastError()).GetDescription());
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicHeapArray<DISPLAYCONFIG_PATH_INFO> path_info;
|
||||||
|
DynamicHeapArray<DISPLAYCONFIG_MODE_INFO> mode_info;
|
||||||
|
|
||||||
|
// I guess this could fail if it changes inbetween two calls... unlikely.
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
UINT32 path_size = 0, mode_size = 0;
|
||||||
|
LONG res = GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &path_size, &mode_size);
|
||||||
|
if (res != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
Log_ErrorFmt("{}() failed: {}", "GetDisplayConfigBufferSizes", Error::CreateWin32(res).GetDescription());
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
path_info.resize(path_size);
|
||||||
|
mode_info.resize(mode_size);
|
||||||
|
res =
|
||||||
|
QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &path_size, path_info.data(), &mode_size, mode_info.data(), nullptr);
|
||||||
|
if (res == ERROR_SUCCESS)
|
||||||
|
break;
|
||||||
|
if (res != ERROR_INSUFFICIENT_BUFFER)
|
||||||
|
{
|
||||||
|
Log_ErrorFmt("{}() failed: {}", "QueryDisplayConfig", Error::CreateWin32(res).GetDescription());
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const DISPLAYCONFIG_PATH_INFO& pi : path_info)
|
||||||
|
{
|
||||||
|
DISPLAYCONFIG_SOURCE_DEVICE_NAME sdn = {.header = {.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME,
|
||||||
|
.size = sizeof(DISPLAYCONFIG_SOURCE_DEVICE_NAME),
|
||||||
|
.adapterId = pi.sourceInfo.adapterId,
|
||||||
|
.id = pi.sourceInfo.id}};
|
||||||
|
LONG res = DisplayConfigGetDeviceInfo(&sdn.header);
|
||||||
|
if (res != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
Log_ErrorFmt("{}() failed: {}", "DisplayConfigGetDeviceInfo", Error::CreateWin32(res).GetDescription());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::wcscmp(sdn.viewGdiDeviceName, mi.szDevice) == 0)
|
||||||
|
{
|
||||||
|
// Found the monitor!
|
||||||
|
return static_cast<float>(static_cast<double>(pi.targetInfo.refreshRate.Numerator) /
|
||||||
|
static_cast<double>(pi.targetInfo.refreshRate.Denominator));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::optional<float> GetRefreshRateFromDWM(HWND hwnd)
|
||||||
{
|
{
|
||||||
BOOL composition_enabled;
|
BOOL composition_enabled;
|
||||||
if (FAILED(DwmIsCompositionEnabled(&composition_enabled)))
|
if (FAILED(DwmIsCompositionEnabled(&composition_enabled)))
|
||||||
return false;
|
return std::nullopt;
|
||||||
|
|
||||||
DWM_TIMING_INFO ti = {};
|
DWM_TIMING_INFO ti = {};
|
||||||
ti.cbSize = sizeof(ti);
|
ti.cbSize = sizeof(ti);
|
||||||
|
@ -37,20 +112,19 @@ static bool GetRefreshRateFromDWM(HWND hwnd, float* refresh_rate)
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
if (ti.rateRefresh.uiNumerator == 0 || ti.rateRefresh.uiDenominator == 0)
|
if (ti.rateRefresh.uiNumerator == 0 || ti.rateRefresh.uiDenominator == 0)
|
||||||
return false;
|
return std::nullopt;
|
||||||
|
|
||||||
*refresh_rate = static_cast<float>(ti.rateRefresh.uiNumerator) / static_cast<float>(ti.rateRefresh.uiDenominator);
|
return static_cast<float>(ti.rateRefresh.uiNumerator) / static_cast<float>(ti.rateRefresh.uiDenominator);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool GetRefreshRateFromMonitor(HWND hwnd, float* refresh_rate)
|
static std::optional<float> GetRefreshRateFromMonitor(HWND hwnd)
|
||||||
{
|
{
|
||||||
HMONITOR mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
HMONITOR mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||||
if (!mon)
|
if (!mon)
|
||||||
return false;
|
return std::nullopt;
|
||||||
|
|
||||||
MONITORINFOEXW mi = {};
|
MONITORINFOEXW mi = {};
|
||||||
mi.cbSize = sizeof(mi);
|
mi.cbSize = sizeof(mi);
|
||||||
|
@ -61,38 +135,39 @@ static bool GetRefreshRateFromMonitor(HWND hwnd, float* refresh_rate)
|
||||||
|
|
||||||
// 0/1 are reserved for "defaults".
|
// 0/1 are reserved for "defaults".
|
||||||
if (EnumDisplaySettingsW(mi.szDevice, ENUM_CURRENT_SETTINGS, &dm) && dm.dmDisplayFrequency > 1)
|
if (EnumDisplaySettingsW(mi.szDevice, ENUM_CURRENT_SETTINGS, &dm) && dm.dmDisplayFrequency > 1)
|
||||||
{
|
return static_cast<float>(dm.dmDisplayFrequency);
|
||||||
*refresh_rate = static_cast<float>(dm.dmDisplayFrequency);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi, float* refresh_rate)
|
std::optional<float> WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi)
|
||||||
{
|
{
|
||||||
|
std::optional<float> ret;
|
||||||
if (wi.type != Type::Win32 || !wi.window_handle)
|
if (wi.type != Type::Win32 || !wi.window_handle)
|
||||||
return false;
|
return ret;
|
||||||
|
|
||||||
// Try DWM first, then fall back to integer values.
|
// Try DWM first, then fall back to integer values.
|
||||||
const HWND hwnd = static_cast<HWND>(wi.window_handle);
|
const HWND hwnd = static_cast<HWND>(wi.window_handle);
|
||||||
return GetRefreshRateFromDWM(hwnd, refresh_rate) || GetRefreshRateFromMonitor(hwnd, refresh_rate);
|
ret = GetRefreshRateFromDisplayConfig(hwnd);
|
||||||
|
if (!ret.has_value())
|
||||||
|
{
|
||||||
|
ret = GetRefreshRateFromDWM(hwnd);
|
||||||
|
if (!ret.has_value())
|
||||||
|
ret = GetRefreshRateFromMonitor(hwnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#ifdef ENABLE_X11
|
#ifdef ENABLE_X11
|
||||||
|
|
||||||
#include "common/scoped_guard.h"
|
|
||||||
#include "common/log.h"
|
|
||||||
|
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <X11/Xutil.h>
|
#include <X11/Xutil.h>
|
||||||
#include <X11/extensions/Xrandr.h>
|
#include <X11/extensions/Xrandr.h>
|
||||||
|
|
||||||
Log_SetChannel(WindowInfo);
|
|
||||||
|
|
||||||
// Helper class for managing X errors
|
// Helper class for managing X errors
|
||||||
namespace {
|
namespace {
|
||||||
class X11InhibitErrors;
|
class X11InhibitErrors;
|
||||||
|
@ -135,12 +210,12 @@ private:
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
static bool GetRefreshRateFromXRandR(const WindowInfo& wi, float* refresh_rate)
|
static std::optional<float> GetRefreshRateFromXRandR(const WindowInfo& wi)
|
||||||
{
|
{
|
||||||
Display* display = static_cast<Display*>(wi.display_connection);
|
Display* display = static_cast<Display*>(wi.display_connection);
|
||||||
Window window = static_cast<Window>(reinterpret_cast<uintptr_t>(wi.window_handle));
|
Window window = static_cast<Window>(reinterpret_cast<uintptr_t>(wi.window_handle));
|
||||||
if (!display || !window)
|
if (!display || !window)
|
||||||
return false;
|
return std::nullopt;
|
||||||
|
|
||||||
X11InhibitErrors inhibiter;
|
X11InhibitErrors inhibiter;
|
||||||
|
|
||||||
|
@ -148,7 +223,7 @@ static bool GetRefreshRateFromXRandR(const WindowInfo& wi, float* refresh_rate)
|
||||||
if (!res)
|
if (!res)
|
||||||
{
|
{
|
||||||
Log_ErrorPrint("XRRGetScreenResources() failed");
|
Log_ErrorPrint("XRRGetScreenResources() failed");
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedGuard res_guard([res]() { XRRFreeScreenResources(res); });
|
ScopedGuard res_guard([res]() { XRRFreeScreenResources(res); });
|
||||||
|
@ -158,7 +233,7 @@ static bool GetRefreshRateFromXRandR(const WindowInfo& wi, float* refresh_rate)
|
||||||
if (num_monitors < 0)
|
if (num_monitors < 0)
|
||||||
{
|
{
|
||||||
Log_ErrorPrint("XRRGetMonitors() failed");
|
Log_ErrorPrint("XRRGetMonitors() failed");
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
else if (num_monitors > 1)
|
else if (num_monitors > 1)
|
||||||
{
|
{
|
||||||
|
@ -169,7 +244,7 @@ static bool GetRefreshRateFromXRandR(const WindowInfo& wi, float* refresh_rate)
|
||||||
if (mi->noutput <= 0)
|
if (mi->noutput <= 0)
|
||||||
{
|
{
|
||||||
Log_ErrorPrint("Monitor has no outputs");
|
Log_ErrorPrint("Monitor has no outputs");
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
else if (mi->noutput > 1)
|
else if (mi->noutput > 1)
|
||||||
{
|
{
|
||||||
|
@ -180,7 +255,7 @@ static bool GetRefreshRateFromXRandR(const WindowInfo& wi, float* refresh_rate)
|
||||||
if (!oi)
|
if (!oi)
|
||||||
{
|
{
|
||||||
Log_ErrorPrint("XRRGetOutputInfo() failed");
|
Log_ErrorPrint("XRRGetOutputInfo() failed");
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedGuard oi_guard([oi]() { XRRFreeOutputInfo(oi); });
|
ScopedGuard oi_guard([oi]() { XRRFreeOutputInfo(oi); });
|
||||||
|
@ -189,7 +264,7 @@ static bool GetRefreshRateFromXRandR(const WindowInfo& wi, float* refresh_rate)
|
||||||
if (!ci)
|
if (!ci)
|
||||||
{
|
{
|
||||||
Log_ErrorPrint("XRRGetCrtcInfo() failed");
|
Log_ErrorPrint("XRRGetCrtcInfo() failed");
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedGuard ci_guard([ci]() { XRRFreeCrtcInfo(ci); });
|
ScopedGuard ci_guard([ci]() { XRRFreeCrtcInfo(ci); });
|
||||||
|
@ -206,30 +281,29 @@ static bool GetRefreshRateFromXRandR(const WindowInfo& wi, float* refresh_rate)
|
||||||
if (!mode)
|
if (!mode)
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Failed to look up mode %d (of %d)", static_cast<int>(ci->mode), res->nmode);
|
Log_ErrorPrintf("Failed to look up mode %d (of %d)", static_cast<int>(ci->mode), res->nmode);
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode->dotClock == 0 || mode->hTotal == 0 || mode->vTotal == 0)
|
if (mode->dotClock == 0 || mode->hTotal == 0 || mode->vTotal == 0)
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Modeline is invalid: %ld/%d/%d", mode->dotClock, mode->hTotal, mode->vTotal);
|
Log_ErrorPrintf("Modeline is invalid: %ld/%d/%d", mode->dotClock, mode->hTotal, mode->vTotal);
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
*refresh_rate =
|
return static_cast<float>(static_cast<double>(mode->dotClock) /
|
||||||
static_cast<double>(mode->dotClock) / (static_cast<double>(mode->hTotal) * static_cast<double>(mode->vTotal));
|
(static_cast<double>(mode->hTotal) * static_cast<double>(mode->vTotal)));
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ENABLE_X11
|
#endif // ENABLE_X11
|
||||||
|
|
||||||
bool WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi, float* refresh_rate)
|
std::optional<float> WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi)
|
||||||
{
|
{
|
||||||
#if defined(ENABLE_X11)
|
#if defined(ENABLE_X11)
|
||||||
if (wi.type == WindowInfo::Type::X11)
|
if (wi.type == WindowInfo::Type::X11)
|
||||||
return GetRefreshRateFromXRandR(wi, refresh_rate);
|
return GetRefreshRateFromXRandR(wi);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return false;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -1,10 +1,13 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "gpu_texture.h"
|
#include "gpu_texture.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
// Contains the information required to create a graphics context in a window.
|
// Contains the information required to create a graphics context in a window.
|
||||||
struct WindowInfo
|
struct WindowInfo
|
||||||
{
|
{
|
||||||
|
@ -38,5 +41,5 @@ struct WindowInfo
|
||||||
// Changes the window to be surfaceless (i.e. no handle/size/etc).
|
// Changes the window to be surfaceless (i.e. no handle/size/etc).
|
||||||
void SetSurfaceless();
|
void SetSurfaceless();
|
||||||
|
|
||||||
static bool QueryRefreshRateForWindow(const WindowInfo& wi, float* refresh_rate);
|
static std::optional<float> QueryRefreshRateForWindow(const WindowInfo& wi);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue