Merge pull request #118339 from bruvzg/winrt_color

[Windows] Add support for `AdvancedColorInfo` info and change callback.
This commit is contained in:
Thaddeus Crews
2026-04-16 09:41:19 -05:00
6 changed files with 253 additions and 43 deletions
+7 -2
View File
@@ -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"])
+2
View File
@@ -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",
]
)
+64 -39
View File
@@ -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<RenderingContextDriverD3D12 *>(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<RenderingContextDriverD3D12 *>(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<DISPLAYCONFIG_PATH_INFO> paths;
LocalVector<DISPLAYCONFIG_MODE_INFO> modes;
paths.resize(path_count);
modes.resize(mode_count);
if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &path_count, &mode_count) == ERROR_SUCCESS) {
LocalVector<DISPLAYCONFIG_PATH_INFO> paths;
LocalVector<DISPLAYCONFIG_MODE_INFO> 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<DisplayServerEnums::WindowID, WindowData> &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<List<FileDialogData *>::Element *> to_remove;
for (List<FileDialogData *>::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<DisplayServerEnums::IndicatorID, IndicatorData>::Iterator E = indicators.begin(); E; ++E) {
NOTIFYICONDATAW ndat;
+7 -2
View File
@@ -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<Vector2> mpath;
DisplayServerEnums::ProgressState progress_state = DisplayServerEnums::PROGRESS_STATE_NOPROGRESS;
@@ -556,9 +561,9 @@ class DisplayServerWindows : public DisplayServer {
};
AHashMap<int, ScreenHdrData> 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);
+157
View File
@@ -42,16 +42,99 @@ GODOT_GCC_WARNING_IGNORE("-Wstrict-aliasing")
GODOT_CLANG_WARNING_PUSH
GODOT_CLANG_WARNING_IGNORE("-Wnon-virtual-dtor")
#include <inspectable.h>
#include <winrt/Windows.Foundation.Metadata.h>
#include <winrt/Windows.Graphics.Display.h>
#include <winrt/Windows.System.h>
#include <winrt/Windows.UI.Input.h>
#include <winrt/Windows.UI.ViewManagement.Core.h>
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 <windows.graphics.display.interop.h>
#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<DisplayInformation, IDisplayInformationStaticsInterop>()->GetForWindow(p_window, winrt::guid_of<DisplayInformation>(), 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
+16
View File
@@ -30,7 +30,23 @@
#pragma once
#include "core/typedefs.h"
#include "core/variant/callable.h"
#include "core/variant/variant.h"
#include <windows.h>
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);
};