WindowInfo: Remove QueryRefreshRateForWindow()

No longer needed.
pull/3706/head
Stenzek 6 months ago
parent 13ab70f47f
commit 7485d7ce8c
No known key found for this signature in database

@ -124,8 +124,8 @@ if(NOT WIN32)
if(NOT APPLE)
if(ENABLE_X11)
find_package(X11 REQUIRED)
if (NOT X11_xcb_FOUND OR NOT X11_xcb_randr_FOUND OR NOT X11_X11_xcb_FOUND)
message(FATAL_ERROR "XCB, XCB-randr and X11-xcb are required")
if (NOT X11_xcb_FOUND)
message(FATAL_ERROR "XCB is required")
endif()
endif()

@ -55,9 +55,6 @@ std::optional<std::pair<int, int>> GetViewSizeInPixels(const void* view);
/// Returns the "real" scaling factor for a given view, on its current display.
std::optional<double> GetViewRealScalingFactor(const void* view);
/// Returns the refresh rate of the display the window is placed on.
std::optional<float> GetViewRefreshRate(const void* view, Error* error);
/// Creates metal layer on specified window surface.
void* CreateMetalLayer(void* view, Error* error);

@ -210,62 +210,6 @@ std::optional<double> CocoaTools::GetViewRealScalingFactor(const void* view)
return static_cast<double>(scale);
}
std::optional<float> CocoaTools::GetViewRefreshRate(const void* view, Error* error)
{
if (!view)
return std::nullopt;
if (![NSThread isMainThread])
{
std::optional<float> ret;
dispatch_sync(dispatch_get_main_queue(), [&ret, view, error] { ret = GetViewRefreshRate(view, error); });
return ret;
}
std::optional<float> ret;
NSView* const nsview = (__bridge NSView*)view;
const u32 did = [[[[[nsview window] screen] deviceDescription] valueForKey:@"NSScreenNumber"] unsignedIntValue];
if (CGDisplayModeRef mode = CGDisplayCopyDisplayMode(did))
{
ret = CGDisplayModeGetRefreshRate(mode);
if (ret.value() <= 0.0f)
{
ret.reset();
// Ignore deprecration warnings here. The new APIs don't seem to have something that matches the semantics.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
CVDisplayLinkRef link = nullptr;
if (CVDisplayLinkCreateWithCGDisplay(did, &link) == 0)
{
const CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link);
if (!(time.flags & kCVTimeIsIndefinite) && time.timeValue != 0)
{
ret = static_cast<float>(static_cast<double>(time.timeScale) / static_cast<double>(time.timeValue));
}
else
{
Error::SetStringFmt(error, "Refresh period is invalid (flags=0x{:X}, timeValue={}, timeScale={})",
static_cast<u32>(time.flags), time.timeValue, time.timeScale);
}
}
else
{
Error::SetStringView(error, "CVDisplayLinkCreateWithCGDisplay() failed");
}
#pragma clang diagnostic pop
}
CGDisplayModeRelease(mode);
}
else
{
Error::SetStringView(error, "CGDisplayCopyDisplayMode() failed");
}
return ret;
}
void* CocoaTools::CreateMetalLayer(void* view, Error* error)
{
// Punt off to main thread if we're not calling from it already.

@ -94,11 +94,7 @@ if(ENABLE_X11)
x11_tools.h
)
target_compile_definitions(util PRIVATE "-DENABLE_X11=1")
target_include_directories(util PRIVATE
"${X11_xcb_INCLUDE_PATH}"
"${X11_xcb_randr_INCLUDE_PATH}"
"${X11_X11_xcb_INCLUDE_PATH}"
)
target_include_directories(util PRIVATE "${X11_xcb_INCLUDE_PATH}")
endif()
if(ENABLE_WAYLAND)

@ -4,21 +4,12 @@
#include "window_info.h"
#include "gpu_types.h"
#include "common/assert.h"
#include "common/error.h"
#include "common/heap_array.h"
#include "common/log.h"
#include "common/scoped_guard.h"
#include <numbers>
#include <utility>
LOG_CHANNEL(WindowInfo);
WindowInfo::WindowInfo()
: type(WindowInfoType::Surfaceless), surface_format(GPUTextureFormat::Unknown),
surface_prerotation(WindowInfoPrerotation::Identity), surface_width(0), surface_height(0), surface_refresh_rate(0.0f),
surface_scale(1.0f), display_connection(nullptr), window_handle(nullptr)
surface_prerotation(WindowInfoPrerotation::Identity), surface_width(0), surface_height(0),
surface_refresh_rate(0.0f), surface_scale(1.0f), display_connection(nullptr), window_handle(nullptr)
{
}
@ -33,200 +24,3 @@ float WindowInfo::GetZRotationForPreRotation(WindowInfoPrerotation prerotation)
return rotation_radians[static_cast<size_t>(prerotation)];
}
#if defined(_WIN32)
#include "common/windows_headers.h"
#include <dwmapi.h>
static std::optional<float> GetRefreshRateFromDisplayConfig(HWND hwnd, Error* error)
{
// Partially based on Chromium ui/display/win/display_config_helper.cc.
const HMONITOR monitor = MonitorFromWindow(hwnd, 0);
if (!monitor) [[unlikely]]
{
Error::SetWin32(error, "MonitorFromWindow() failed: ", GetLastError());
return std::nullopt;
}
MONITORINFOEXW mi = {};
mi.cbSize = sizeof(mi);
if (!GetMonitorInfoW(monitor, &mi))
{
Error::SetWin32(error, "GetMonitorInfoW() failed: ", GetLastError());
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)
{
Error::SetWin32(error, "GetDisplayConfigBufferSizes() failed: ", res);
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)
{
Error::SetWin32(error, "QueryDisplayConfig() failed: ", res);
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},
.viewGdiDeviceName = {}};
LONG res = DisplayConfigGetDeviceInfo(&sdn.header);
if (res != ERROR_SUCCESS)
{
Error::SetWin32(error, "DisplayConfigGetDeviceInfo() failed: ", res);
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, Error* error)
{
BOOL composition_enabled;
HRESULT hr = DwmIsCompositionEnabled(&composition_enabled);
if (FAILED(hr))
{
Error::SetHResult(error, "DwmIsCompositionEnabled() failed: ", hr);
return std::nullopt;
}
DWM_TIMING_INFO ti = {};
ti.cbSize = sizeof(ti);
hr = DwmGetCompositionTimingInfo(nullptr, &ti);
if (SUCCEEDED(hr))
{
if (ti.rateRefresh.uiNumerator == 0 || ti.rateRefresh.uiDenominator == 0)
return std::nullopt;
return static_cast<float>(ti.rateRefresh.uiNumerator) / static_cast<float>(ti.rateRefresh.uiDenominator);
}
else
{
Error::SetHResult(error, "DwmGetCompositionTimingInfo() failed: ", hr);
return std::nullopt;
}
}
static std::optional<float> GetRefreshRateFromMonitor(HWND hwnd, Error* error)
{
HMONITOR mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if (!mon)
{
Error::SetWin32(error, "MonitorFromWindow() failed: ", GetLastError());
return std::nullopt;
}
MONITORINFOEXW mi = {};
mi.cbSize = sizeof(mi);
if (GetMonitorInfoW(mon, &mi))
{
DEVMODEW dm = {};
dm.dmSize = sizeof(dm);
// 0/1 are reserved for "defaults".
if (EnumDisplaySettingsW(mi.szDevice, ENUM_CURRENT_SETTINGS, &dm) && dm.dmDisplayFrequency > 1)
{
return static_cast<float>(dm.dmDisplayFrequency);
}
else
{
Error::SetWin32(error, "EnumDisplaySettingsW() failed: ", GetLastError());
return std::nullopt;
}
}
else
{
Error::SetWin32(error, "GetMonitorInfoW() failed: ", GetLastError());
return std::nullopt;
}
}
std::optional<float> WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi, Error* error)
{
std::optional<float> ret;
if (wi.type != WindowInfoType::Win32 || !wi.window_handle)
{
Error::SetStringView(error, "Invalid window type.");
return ret;
}
// Try DWM first, then fall back to integer values.
const HWND hwnd = static_cast<HWND>(wi.window_handle);
Error local_error;
ret = GetRefreshRateFromDisplayConfig(hwnd, &local_error);
if (!ret.has_value())
{
WARNING_LOG("GetRefreshRateFromDisplayConfig() failed: {}", local_error.GetDescription());
ret = GetRefreshRateFromDWM(hwnd, &local_error);
if (!ret.has_value())
{
WARNING_LOG("GetRefreshRateFromDWM() failed: {}", local_error.GetDescription());
ret = GetRefreshRateFromMonitor(hwnd, error);
}
}
return ret;
}
#elif defined(__APPLE__)
#include "common/cocoa_tools.h"
std::optional<float> WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi, Error* error)
{
if (wi.type == WindowInfoType::MacOS)
return CocoaTools::GetViewRefreshRate(wi.window_handle, error);
Error::SetStringView(error, "Invalid window type.");
return std::nullopt;
}
#else
#ifdef ENABLE_X11
#include "x11_tools.h"
#endif
std::optional<float> WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi, Error* error)
{
#if defined(ENABLE_X11)
if (wi.type == WindowInfoType::Xlib || wi.type == WindowInfoType::XCB)
return GetRefreshRateFromXRandR(wi, error);
#endif
Error::SetStringView(error, "Invalid window type.");
return std::nullopt;
}
#endif

@ -5,10 +5,6 @@
#include "common/types.h"
#include <optional>
class Error;
enum class GPUTextureFormat : u8;
enum class WindowInfoType : u8
@ -63,6 +59,4 @@ struct WindowInfo
}
static float GetZRotationForPreRotation(WindowInfoPrerotation prerotation);
static std::optional<float> QueryRefreshRateForWindow(const WindowInfo& wi, Error* error = nullptr);
};

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "x11_tools.h"
@ -8,10 +8,7 @@
#include "common/dynamic_library.h"
#include "common/error.h"
#include "common/log.h"
#include "common/scoped_guard.h"
#include <X11/Xlib-xcb.h>
#include <xcb/randr.h>
#include <xcb/xcb.h>
#include <memory>
@ -42,39 +39,16 @@ namespace dyn_libs {
X(xcb_free_colormap_checked) \
X(xcb_configure_window_checked)
#define XCB_RANDR_FUNCTIONS(X) \
X(xcb_randr_get_screen_resources_reply) \
X(xcb_randr_get_screen_resources) \
X(xcb_randr_get_monitors_reply) \
X(xcb_randr_get_monitors) \
X(xcb_randr_get_monitors_monitors_iterator) \
X(xcb_randr_monitor_info_outputs) \
X(xcb_randr_get_output_info_reply) \
X(xcb_randr_get_output_info) \
X(xcb_randr_get_crtc_info_reply) \
X(xcb_randr_get_crtc_info) \
X(xcb_randr_get_screen_resources_modes_iterator) \
X(xcb_randr_mode_info_next)
#define X11XCB_FUNCTIONS(X) X(XGetXCBConnection)
static bool OpenXcb(Error* error);
static void CloseXcb();
static bool OpenXcbRandR(Error* error);
static void CloseXcbRandR();
static bool OpenX11Xcb(Error* error);
static void CloseX11Xcb();
static void CloseAll();
static DynamicLibrary s_xcb_library;
static DynamicLibrary s_xcb_randr_library;
static DynamicLibrary s_x11xcb_library;
static bool s_close_registered = false;
#define ADD_FUNC(F) static decltype(&::F) F;
XCB_FUNCTIONS(ADD_FUNC);
XCB_RANDR_FUNCTIONS(ADD_FUNC);
X11XCB_FUNCTIONS(ADD_FUNC);
#undef ADD_FUNC
} // namespace dyn_libs
@ -131,92 +105,8 @@ void dyn_libs::CloseXcb()
s_xcb_library.Close();
}
bool dyn_libs::OpenXcbRandR(Error* error)
{
if (s_xcb_randr_library.IsOpen())
return true;
const std::string libname = DynamicLibrary::GetVersionedFilename("xcb-randr", 0);
if (!s_xcb_randr_library.Open(libname.c_str(), error))
{
Error::AddPrefix(error, "Failed to load xcb-randr: ");
return false;
}
#define LOAD_FUNC(F) \
if (!s_xcb_randr_library.GetSymbol(#F, &F)) \
{ \
Error::SetStringFmt(error, "Failed to find function {}", #F); \
CloseXcb(); \
return false; \
}
XCB_RANDR_FUNCTIONS(LOAD_FUNC)
#undef LOAD_FUNC
if (!s_close_registered)
{
s_close_registered = true;
std::atexit(&dyn_libs::CloseAll);
}
return true;
}
void dyn_libs::CloseXcbRandR()
{
#define UNLOAD_FUNC(F) F = nullptr;
XCB_RANDR_FUNCTIONS(UNLOAD_FUNC)
#undef UNLOAD_FUNC
s_xcb_randr_library.Close();
}
bool dyn_libs::OpenX11Xcb(Error* error)
{
if (s_x11xcb_library.IsOpen())
return true;
const std::string libname = DynamicLibrary::GetVersionedFilename("X11-xcb", 1);
if (!s_x11xcb_library.Open(libname.c_str(), error))
{
Error::AddPrefix(error, "Failed to load X11-xcb: ");
return false;
}
#define LOAD_FUNC(F) \
if (!s_x11xcb_library.GetSymbol(#F, &F)) \
{ \
Error::SetStringFmt(error, "Failed to find function {}", #F); \
CloseXcb(); \
return false; \
}
X11XCB_FUNCTIONS(LOAD_FUNC)
#undef LOAD_FUNC
if (!s_close_registered)
{
s_close_registered = true;
std::atexit(&dyn_libs::CloseAll);
}
return true;
}
void dyn_libs::CloseX11Xcb()
{
#define UNLOAD_FUNC(F) F = nullptr;
X11XCB_FUNCTIONS(UNLOAD_FUNC)
#undef UNLOAD_FUNC
s_x11xcb_library.Close();
}
void dyn_libs::CloseAll()
{
CloseX11Xcb();
CloseXcbRandR();
CloseXcb();
}
@ -427,106 +317,3 @@ void X11Window::Resize(u16 width, u16 height)
ERROR_LOG(error.GetDescription());
}
}
std::optional<float> GetRefreshRateFromXRandR(const WindowInfo& wi, Error* error)
{
xcb_connection_t* connection = nullptr;
if (wi.type == WindowInfoType::Xlib)
{
if (!dyn_libs::OpenX11Xcb(error))
return std::nullopt;
connection = dyn_libs::XGetXCBConnection(static_cast<Display*>(wi.display_connection));
}
else if (wi.type == WindowInfoType::XCB)
{
connection = static_cast<xcb_connection_t*>(wi.display_connection);
}
xcb_window_t window = static_cast<xcb_window_t>(reinterpret_cast<uintptr_t>(wi.window_handle));
if (!connection || window == XCB_NONE)
{
Error::SetStringView(error, "Invalid window handle.");
return std::nullopt;
}
if (!dyn_libs::OpenXcb(error) || !dyn_libs::OpenXcbRandR(error))
return std::nullopt;
xcb_generic_error_t* xerror;
XCBPointer<xcb_randr_get_screen_resources_reply_t> gsr(dyn_libs::xcb_randr_get_screen_resources_reply(
connection, dyn_libs::xcb_randr_get_screen_resources(connection, window), &xerror));
if (xerror)
{
SetErrorObject(error, "xcb_randr_get_screen_resources() failed: ", xerror);
return std::nullopt;
}
XCBPointer<xcb_randr_get_monitors_reply_t> gm(dyn_libs::xcb_randr_get_monitors_reply(
connection, dyn_libs::xcb_randr_get_monitors(connection, window, true), &xerror));
if (xerror || gm->nMonitors < 0)
{
SetErrorObject(error, "xcb_randr_get_screen_resources() failed: ", xerror);
return std::nullopt;
}
if (gm->nMonitors > 1)
WARNING_LOG("xcb_randr_get_monitors() returned {} monitors, using first", gm->nMonitors);
if (gm->nOutputs <= 0)
{
Error::SetStringView(error, "Monitor has no outputs");
return std::nullopt;
}
else if (gm->nOutputs > 1)
{
WARNING_LOG("Monitor has {} outputs, using first", gm->nOutputs);
}
xcb_randr_monitor_info_t* monitor_info = dyn_libs::xcb_randr_get_monitors_monitors_iterator(gm.get()).data;
DebugAssert(monitor_info);
xcb_randr_output_t* monitor_outputs = dyn_libs::xcb_randr_monitor_info_outputs(monitor_info);
DebugAssert(monitor_outputs);
XCBPointer<xcb_randr_get_output_info_reply_t> goi(dyn_libs::xcb_randr_get_output_info_reply(
connection, dyn_libs::xcb_randr_get_output_info(connection, monitor_outputs[0], 0), &xerror));
if (xerror)
{
SetErrorObject(error, "xcb_randr_get_output_info() failed: ", xerror);
return std::nullopt;
}
XCBPointer<xcb_randr_get_crtc_info_reply_t> gci(dyn_libs::xcb_randr_get_crtc_info_reply(
connection, dyn_libs::xcb_randr_get_crtc_info(connection, goi->crtc, 0), &xerror));
if (xerror)
{
SetErrorObject(error, "xcb_randr_get_crtc_info_reply() failed: ", xerror);
return std::nullopt;
}
xcb_randr_mode_info_t* mode = nullptr;
for (xcb_randr_mode_info_iterator_t it = dyn_libs::xcb_randr_get_screen_resources_modes_iterator(gsr.get());
it.rem != 0; dyn_libs::xcb_randr_mode_info_next(&it))
{
if (it.data->id == gci->mode)
{
mode = it.data;
break;
}
}
if (!mode)
{
Error::SetStringFmt(error, "Failed to look up mode ID {}", static_cast<int>(gci->mode));
return std::nullopt;
}
if (mode->dot_clock == 0 || mode->htotal == 0 || mode->vtotal == 0)
{
ERROR_LOG("Modeline is invalid: {}/{}/{}", mode->dot_clock, mode->htotal, mode->vtotal);
return std::nullopt;
}
return static_cast<float>(static_cast<double>(mode->dot_clock) /
(static_cast<double>(mode->htotal) * static_cast<double>(mode->vtotal)));
}

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once
@ -41,5 +41,3 @@ private:
u16 m_width = 0;
u16 m_height = 0;
};
std::optional<float> GetRefreshRateFromXRandR(const WindowInfo& wi, Error* error);

Loading…
Cancel
Save