From 778e0a7879e51b1fba0c0fdab43a90dc78cce61b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pa=CC=84vels=20Nadtoc=CC=8Cajevs?= <7645683+bruvzg@users.noreply.github.com> Date: Thu, 9 Apr 2026 13:54:54 +0300 Subject: [PATCH] [Windows] Add support for AdvancedColorInfo info and change callback. Co-authored-by: Allen Pestaluky --- platform/windows/SCsub | 9 +- platform/windows/detect.py | 2 + platform/windows/display_server_windows.cpp | 103 ++++++++----- platform/windows/display_server_windows.h | 9 +- platform/windows/winrt_utils.cpp | 157 ++++++++++++++++++++ platform/windows/winrt_utils.h | 16 ++ 6 files changed, 253 insertions(+), 43 deletions(-) diff --git a/platform/windows/SCsub b/platform/windows/SCsub index a29d0a3389..f83fed3ded 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -8,7 +8,7 @@ from pathlib import Path import platform_windows_builders -from methods import redirect_emitter +from methods import get_compiler_version, redirect_emitter sources = [] @@ -92,8 +92,13 @@ if env["winrt"]: env_winrt["CXXFLAGS"].remove("-fno-exceptions") env_winrt.Append(CXXFLAGS=["-fexceptions"]) else: + cc_version = get_compiler_version(env) + cc_version_major = cc_version["major"] if not env["use_llvm"]: - env_winrt.Append(CXXFLAGS=["/await"]) + if cc_version_major >= 18: + env_winrt.Append(CXXFLAGS=["/await:strict"]) # /await is deprecated in MSVC 2026+ + else: + env_winrt.Append(CXXFLAGS=["/await"]) if "/std:c++17" in env_winrt["CXXFLAGS"]: env_winrt["CXXFLAGS"].remove("/std:c++17") env_winrt.Append(CXXFLAGS=["/std:c++20"]) diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 35cb2fe25f..7a97a6e153 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -427,6 +427,7 @@ def configure_msvc(env: "SConsEnvironment"): "ntdll", "hid", "mincore", + "coremessaging", ] if env.debug_features: @@ -823,6 +824,7 @@ def configure_mingw(env: "SConsEnvironment"): "ntdll", "hid", "mincore", + "coremessaging", ] ) diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index ddc1d3649e..567f994cb1 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -3407,41 +3407,47 @@ HWND DisplayServerWindows::_find_window_from_process_id(ProcessID p_pid, HWND p_ } // Get screen HDR capabilities for internal use only. -DisplayServerWindows::ScreenHdrData DisplayServerWindows::_get_screen_hdr_data(int p_screen, bool p_include_sdr_white_level) const { +DisplayServerWindows::ScreenHdrData DisplayServerWindows::_get_screen_hdr_data(DisplayServerEnums::WindowID p_window, bool p_include_sdr_white_level) const { ScreenHdrData data; - HMONITOR monitor = _get_hmonitor_of_screen(p_screen); - if (!monitor) { - return data; - } + const WindowData &wd = windows[p_window]; + if (WinRTUtils::window_has_display_info(wd.wrt_wd)) { + WinRTUtils::window_get_advanced_color_info(wd.wrt_wd, data.hdr_supported, data.min_luminance, data.max_luminance, data.max_average_luminance, data.sdr_white_level); + } else { + int screen = window_get_current_screen(p_window); + HMONITOR monitor = _get_hmonitor_of_screen(screen); + if (!monitor) { + return data; + } #ifdef D3D12_ENABLED - // A dynamic cast is used here because the rendering context is not an Object and Object:cast is not supported. - RenderingContextDriverD3D12 *rendering_context_d3d12 = dynamic_cast(rendering_context); - if (rendering_context_d3d12) { - IDXGIFactory2 *dxgi_factory = rendering_context_d3d12->dxgi_factory_get(); + // A dynamic cast is used here because the rendering context is not an Object and Object:cast is not supported. + RenderingContextDriverD3D12 *rendering_context_d3d12 = dynamic_cast(rendering_context); + if (rendering_context_d3d12) { + IDXGIFactory2 *dxgi_factory = rendering_context_d3d12->dxgi_factory_get(); - DXGI_OUTPUT_DESC1 desc; - if (_get_monitor_desc(monitor, dxgi_factory, desc)) { - data.hdr_supported = desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; - data.min_luminance = desc.MinLuminance; - data.max_luminance = desc.MaxLuminance; - data.max_average_luminance = desc.MaxFullFrameLuminance; + DXGI_OUTPUT_DESC1 desc; + if (_get_monitor_desc(monitor, dxgi_factory, desc)) { + data.hdr_supported = desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + data.min_luminance = desc.MinLuminance; + data.max_luminance = desc.MaxLuminance; + data.max_average_luminance = desc.MaxFullFrameLuminance; + } } - } #endif // D3D12_ENABLED - if (p_include_sdr_white_level) { - uint32_t path_count = 0; - uint32_t mode_count = 0; + if (p_include_sdr_white_level) { + uint32_t path_count = 0; + uint32_t mode_count = 0; - if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &path_count, &mode_count) == ERROR_SUCCESS) { - LocalVector paths; - LocalVector modes; - paths.resize(path_count); - modes.resize(mode_count); + if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &path_count, &mode_count) == ERROR_SUCCESS) { + LocalVector paths; + LocalVector modes; + paths.resize(path_count); + modes.resize(mode_count); - if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &path_count, paths.ptr(), &mode_count, modes.ptr(), nullptr) == ERROR_SUCCESS) { - data.sdr_white_level = _get_sdr_white_level_for_hmonitor(monitor, paths); + if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &path_count, paths.ptr(), &mode_count, modes.ptr(), nullptr) == ERROR_SUCCESS) { + data.sdr_white_level = _get_sdr_white_level_for_hmonitor(monitor, paths); + } } } } @@ -3449,6 +3455,13 @@ DisplayServerWindows::ScreenHdrData DisplayServerWindows::_get_screen_hdr_data(i return data; } +void DisplayServerWindows::_winrt_adv_color_info_cb(DisplayServerEnums::WindowID p_id) { + WindowData &wd = windows[p_id]; + + DisplayServerWindows::ScreenHdrData data = _get_screen_hdr_data(p_id, true); + _update_hdr_output_for_window(p_id, wd, data); +} + void DisplayServerWindows::_update_hdr_output_for_window(DisplayServerEnums::WindowID p_window, const WindowData &p_window_data, ScreenHdrData p_screen_data) { #ifdef RD_ENABLED if (rendering_context) { @@ -3494,15 +3507,18 @@ void DisplayServerWindows::_update_hdr_output_for_window(DisplayServerEnums::Win #endif // RD_ENABLED } -void DisplayServerWindows::_update_hdr_output_for_tracked_windows(bool p_include_sdr_white_level) { +void DisplayServerWindows::_legacy_update_hdr_output_for_tracked_windows(bool p_include_sdr_white_level) { hdr_output_cache.clear(); for (const KeyValue &E : windows) { + if (WinRTUtils::window_has_display_info(E.value.wrt_wd)) { + continue; // Updated by "_winrt_adv_color_info_cb" callback. + } if (E.value.hdr_output_requested) { int screen = window_get_current_screen(E.key); ScreenHdrData data; if (!hdr_output_cache.has(screen)) { - data = _get_screen_hdr_data(screen, p_include_sdr_white_level); + data = _get_screen_hdr_data(E.key, p_include_sdr_white_level); hdr_output_cache.insert(screen, data); } else { data = hdr_output_cache[screen]; @@ -4211,7 +4227,7 @@ void DisplayServerWindows::process_events() { // because the only way to adjust this is to leave the Godot Window and adjust the SDR/HDR // Content Brightness Windows display setting. This means the user must return to the Godot // window, which triggers a WM_WINDOWPOSCHANGED event. - _update_hdr_output_for_tracked_windows(false); + _legacy_update_hdr_output_for_tracked_windows(false); LocalVector::Element *> to_remove; for (List::Element *E = file_dialogs.front(); E; E = E->next()) { @@ -4737,8 +4753,7 @@ bool DisplayServerWindows::window_is_hdr_output_supported(DisplayServerEnums::Wi #endif // The window supports HDR if the screen it is on supports HDR. - int screen = window_get_current_screen(p_window); - DisplayServerWindows::ScreenHdrData data = _get_screen_hdr_data(screen, false); + DisplayServerWindows::ScreenHdrData data = _get_screen_hdr_data(p_window, false); return data.hdr_supported; } @@ -4752,8 +4767,7 @@ void DisplayServerWindows::window_request_hdr_output(const bool p_enable, Displa WindowData &wd = windows[p_window]; wd.hdr_output_requested = p_enable; - int screen = window_get_current_screen(p_window); - DisplayServerWindows::ScreenHdrData data = _get_screen_hdr_data(screen, true); + DisplayServerWindows::ScreenHdrData data = _get_screen_hdr_data(p_window, true); _update_hdr_output_for_window(p_window, wd, data); } @@ -4789,8 +4803,7 @@ void DisplayServerWindows::window_set_hdr_output_reference_luminance(const float // Negative luminance means auto-adjust if (wd.hdr_output_reference_luminance < 0.0f) { - int screen = window_get_current_screen(p_window); - DisplayServerWindows::ScreenHdrData data = _get_screen_hdr_data(screen, true); + DisplayServerWindows::ScreenHdrData data = _get_screen_hdr_data(p_window, true); _update_hdr_output_for_window(p_window, wd, data); } else { // Otherwise, apply the requested luminance @@ -4835,8 +4848,7 @@ void DisplayServerWindows::window_set_hdr_output_max_luminance(const float p_max // Negative luminance means auto-adjust if (wd.hdr_output_max_luminance < 0.0f) { - int screen = window_get_current_screen(p_window); - DisplayServerWindows::ScreenHdrData data = _get_screen_hdr_data(screen, true); + DisplayServerWindows::ScreenHdrData data = _get_screen_hdr_data(p_window, true); _update_hdr_output_for_window(p_window, wd, data); } else { // Otherwise, apply the requested luminance @@ -6356,7 +6368,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA case WM_DISPLAYCHANGE: { // Update HDR capabilities and reference luminance when display changes. - _update_hdr_output_for_tracked_windows(true); + _legacy_update_hdr_output_for_tracked_windows(true); } break; case WM_WINDOWPOSCHANGED: { @@ -6497,7 +6509,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA // Update HDR capabilities and reference luminance when window moves to different screen. // Also update when Godot has regained focus because the user may have adjusted their SDR white // level while Godot was not in focus. - _update_hdr_output_for_tracked_windows(true); + _legacy_update_hdr_output_for_tracked_windows(true); // Return here to prevent WM_MOVE and WM_SIZE from being sent // See: https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-windowposchanged#remarks @@ -7036,6 +7048,10 @@ Error DisplayServerWindows::_create_window(DisplayServerEnums::WindowID p_window wd.parent_hwnd = p_parent_hwnd; + if (has_winrt_queue) { + wd.wrt_wd = WinRTUtils::create_wd(wd.hWnd, callable_mp(this, &DisplayServerWindows::_winrt_adv_color_info_cb), wd.id); + } + // Detach the input queue from the parent window. // This prevents the embedded window from waiting on the main window's input queue, // causing lags input lags when resizing or moving the main window. @@ -7228,6 +7244,9 @@ void DisplayServerWindows::_destroy_window(DisplayServerEnums::WindowID p_window wd.icon_small = nullptr; } + if (has_winrt_queue) { + WinRTUtils::destroy_wd(wd.wrt_wd); + } DestroyWindow(wd.hWnd); windows.erase(p_window_id); } @@ -7565,6 +7584,8 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Dis } native_menu = memnew(NativeMenuWindows); + has_winrt_queue = WinRTUtils::create_queue(); + // Enforce default keep screen on value. screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on")); @@ -8238,6 +8259,10 @@ DisplayServerWindows::~DisplayServerWindows() { cursors_cache.clear(); + if (has_winrt_queue) { + WinRTUtils::destroy_queue(); + } + // Destroy all status indicators. for (HashMap::Iterator E = indicators.begin(); E; ++E) { NOTIFYICONDATAW ndat; diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index e789de9f5f..847dba5d65 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -184,6 +184,7 @@ typedef struct { class DropTargetWindows; class NativeMenuWindows; class TTS_Windows; +class WinRTWindowData; #ifndef WDA_EXCLUDEFROMCAPTURE #define WDA_EXCLUDEFROMCAPTURE 0x00000011 @@ -282,9 +283,13 @@ class DisplayServerWindows : public DisplayServer { NativeMenuWindows *native_menu = nullptr; ITaskbarList3 *taskbar = nullptr; + bool has_winrt_queue = false; + void _winrt_adv_color_info_cb(DisplayServerEnums::WindowID p_id); + struct WindowData { HWND hWnd; DisplayServerEnums::WindowID id; + WinRTWindowData *wrt_wd = nullptr; Vector mpath; DisplayServerEnums::ProgressState progress_state = DisplayServerEnums::PROGRESS_STATE_NOPROGRESS; @@ -556,9 +561,9 @@ class DisplayServerWindows : public DisplayServer { }; AHashMap hdr_output_cache; - ScreenHdrData _get_screen_hdr_data(int p_screen, bool p_include_sdr_white_level) const; + ScreenHdrData _get_screen_hdr_data(DisplayServerEnums::WindowID p_window, bool p_include_sdr_white_level) const; void _update_hdr_output_for_window(DisplayServerEnums::WindowID p_window, const WindowData &p_window_data, ScreenHdrData p_screen_data); - void _update_hdr_output_for_tracked_windows(bool p_include_sdr_white_level); + void _legacy_update_hdr_output_for_tracked_windows(bool p_include_sdr_white_level); public: LRESULT WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); diff --git a/platform/windows/winrt_utils.cpp b/platform/windows/winrt_utils.cpp index a03567ec87..c289ca0f50 100644 --- a/platform/windows/winrt_utils.cpp +++ b/platform/windows/winrt_utils.cpp @@ -42,16 +42,99 @@ GODOT_GCC_WARNING_IGNORE("-Wstrict-aliasing") GODOT_CLANG_WARNING_PUSH GODOT_CLANG_WARNING_IGNORE("-Wnon-virtual-dtor") +#include #include +#include +#include #include #include +enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE { + DQTAT_COM_NONE = 0, + DQTAT_COM_ASTA = 1, + DQTAT_COM_STA = 2 +}; + +enum DISPATCHERQUEUE_THREAD_TYPE { + DQTYPE_THREAD_DEDICATED = 1, + DQTYPE_THREAD_CURRENT = 2, +}; + +struct DispatcherQueueOptions { + DWORD dwSize; + DISPATCHERQUEUE_THREAD_TYPE threadType; + DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType; +}; + +extern "C" HRESULT __declspec(dllexport) WINAPI CreateDispatcherQueueController(DispatcherQueueOptions options, void *dispatcherQueueController); + +#ifndef E_BOUNDS +#define E_BOUNDS _HRESULT_TYPEDEF_(0x8000000B) +#endif // E_BOUNDS + +#if defined __MINGW32__ || defined __MINGW64__ + +#ifndef __IDisplayInformationStaticsInterop_INTERFACE_DEFINED__ +#define __IDisplayInformationStaticsInterop_INTERFACE_DEFINED__ + +// clang-format off +MIDL_INTERFACE("7449121c-382b-4705-8da7-a795ba482013") +IDisplayInformationStaticsInterop : public IInspectable { +public: + virtual HRESULT STDMETHODCALLTYPE GetForWindow( + HWND window, + REFIID riid, + void **displayInfo) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetForMonitor( + HMONITOR monitor, + REFIID riid, + void **displayInfo) = 0; +}; +// clang-format on +#ifdef __CRT_UUID_DECL +__CRT_UUID_DECL(IDisplayInformationStaticsInterop, 0x7449121c, 0x382b, 0x4705, 0x8d, 0xa7, 0xa7, 0x95, 0xba, 0x48, 0x20, 0x13) +#endif // __CRT_UUID_DECL + +#endif // __IDisplayInformationStaticsInterop_INTERFACE_DEFINED__ + +#else // defined __MINGW32__ || defined __MINGW64__ + +#include + +#endif // defined __MINGW32__ || defined __MINGW64__ + GODOT_GCC_WARNING_POP GODOT_CLANG_WARNING_POP +using namespace winrt::Windows::Graphics::Display; +using namespace winrt::Windows::System; +using namespace winrt::Windows::Foundation; using namespace winrt::Windows::Foundation::Metadata; using namespace winrt::Windows::UI::ViewManagement::Core; +DispatcherQueueController controller{ nullptr }; + +class WinRTWindowData { + friend class WinRTUtils; + + int64_t id = 0; + bool has_disp_info = false; + DisplayInformation disp_info{ nullptr }; + Callable cb; + winrt::event_token token{}; +}; + +bool WinRTUtils::create_queue() { + DispatcherQueueOptions options{ sizeof(options), DQTYPE_THREAD_CURRENT, DQTAT_COM_NONE }; + HRESULT res = CreateDispatcherQueueController(options, winrt::put_abi(controller)); + return SUCCEEDED(res); +} + +void WinRTUtils::destroy_queue() { + controller.ShutdownQueueAsync(); +} + bool WinRTUtils::try_show_onecore_emoji_picker() { if (ApiInformation::IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 7)) { // Windows 10, 1809+ return CoreInputView::GetForCurrentView().TryShow(CoreInputViewKind::Emoji); @@ -59,10 +142,84 @@ bool WinRTUtils::try_show_onecore_emoji_picker() { return false; } +bool WinRTUtils::window_has_display_info(const WinRTWindowData *p_data) { + if (p_data) { + return p_data->has_disp_info; + } else { + return false; + } +} + +void WinRTUtils::window_get_advanced_color_info(const WinRTWindowData *p_data, bool &r_hdr_supported, float &r_min_luminance, float &r_max_luminance, float &r_max_average_luminance, float &r_sdr_white_level) { + if (p_data && p_data->has_disp_info) { + Dictionary info; + + AdvancedColorInfo adv_info = p_data->disp_info.GetAdvancedColorInfo(); + + r_hdr_supported = (adv_info.CurrentAdvancedColorKind() == AdvancedColorKind::HighDynamicRange); + r_min_luminance = adv_info.MinLuminanceInNits(); + r_max_luminance = adv_info.MaxLuminanceInNits(); + r_max_average_luminance = adv_info.MaxAverageFullFrameLuminanceInNits(); + r_sdr_white_level = adv_info.SdrWhiteLevelInNits(); + } +} + +WinRTWindowData *WinRTUtils::create_wd(HWND p_window, const Callable &p_color_cb, int64_t p_window_id) { + WinRTWindowData *wd = memnew(WinRTWindowData); + + wd->id = p_window_id; + wd->cb = p_color_cb; + if (ApiInformation::IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 6)) { + try { + HRESULT res = winrt::get_activation_factory()->GetForWindow(p_window, winrt::guid_of(), winrt::put_abi(wd->disp_info)); + if (res == S_OK && wd->disp_info) { + wd->has_disp_info = true; + wd->token = wd->disp_info.AdvancedColorInfoChanged([wd](const DisplayInformation &p_sender, auto &&) { + wd->cb.call_deferred(wd->id); + }); + } + } catch (...) { + memdelete(wd); + return nullptr; + } + } + + return wd; +} + +void WinRTUtils::destroy_wd(WinRTWindowData *p_data) { + if (p_data) { + if (p_data->token) { + p_data->disp_info.AdvancedColorInfoChanged(p_data->token); + } + p_data->disp_info = nullptr; + + memdelete(p_data); + } +} + #else bool WinRTUtils::try_show_onecore_emoji_picker() { return false; } +bool WinRTUtils::create_queue() { + return false; +} + +void WinRTUtils::destroy_queue() {} + +bool WinRTUtils::window_has_display_info(const WinRTWindowData *p_data) { + return false; +} + +void WinRTUtils::window_get_advanced_color_info(const WinRTWindowData *p_data, bool &r_hdr_supported, float &r_min_luminance, float &r_max_luminance, float &r_max_average_luminance, float &r_sdr_white_level) {} + +WinRTWindowData *WinRTUtils::create_wd(HWND p_window, const Callable &p_color_cb, int64_t p_window_id) { + return nullptr; +} + +void WinRTUtils::destroy_wd(WinRTWindowData *p_data) {} + #endif diff --git a/platform/windows/winrt_utils.h b/platform/windows/winrt_utils.h index d59d15a2d1..ea4f2b709f 100644 --- a/platform/windows/winrt_utils.h +++ b/platform/windows/winrt_utils.h @@ -30,7 +30,23 @@ #pragma once +#include "core/typedefs.h" +#include "core/variant/callable.h" +#include "core/variant/variant.h" + +#include + +class WinRTWindowData; + class WinRTUtils { public: static bool try_show_onecore_emoji_picker(); + + static bool create_queue(); + static void destroy_queue(); + + static WinRTWindowData *create_wd(HWND p_window, const Callable &p_color_cb, int64_t p_window_id); + static bool window_has_display_info(const WinRTWindowData *p_data); + static void window_get_advanced_color_info(const WinRTWindowData *p_data, bool &r_hdr_supported, float &r_min_luminance, float &r_max_luminance, float &r_max_average_luminance, float &r_sdr_white_level); + static void destroy_wd(WinRTWindowData *p_data); };