From 628c81d2d9a2cf05541a8d95dd99f6349aca851d Mon Sep 17 00:00:00 2001
From: bruvzg <7645683+bruvzg@users.noreply.github.com>
Date: Fri, 3 May 2024 11:48:46 +0300
Subject: [PATCH] [DisplayServer] Add method to check if window transparency is
supported and enabled.
---
doc/classes/DisplayServer.xml | 8 +++++++-
.../d3d12/rendering_device_driver_d3d12.cpp | 19 +++++++++++++++++++
drivers/d3d12/rendering_device_driver_d3d12.h | 3 +++
.../vulkan/rendering_device_driver_vulkan.cpp | 8 ++++++++
.../vulkan/rendering_device_driver_vulkan.h | 3 +++
platform/linuxbsd/x11/display_server_x11.cpp | 16 ++++++++++++++++
platform/linuxbsd/x11/display_server_x11.h | 2 ++
platform/macos/display_server_macos.h | 2 ++
platform/macos/display_server_macos.mm | 7 +++++++
platform/windows/display_server_windows.cpp | 13 +++++++++++++
platform/windows/display_server_windows.h | 2 ++
servers/display_server.cpp | 2 ++
servers/display_server.h | 2 ++
servers/rendering/rendering_device.cpp | 4 ++++
servers/rendering/rendering_device.h | 2 ++
servers/rendering/rendering_device_driver.h | 2 ++
16 files changed, 94 insertions(+), 1 deletion(-)
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 688e1b70cae..e157cbb96cf 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -930,6 +930,12 @@
Returns [code]true[/code] if touch events are available (Android or iOS), the capability is detected on the Web platform or if [member ProjectSettings.input_devices/pointing/emulate_touch_from_mouse] is [code]true[/code].
+
+
+
+ Returns [code]true[/code] if the window background can be made transparent. This method returns [code]false[/code] if [member ProjectSettings.display/window/per_pixel_transparency/allowed] is set to [code]false[/code], or if transparency is not supported by the renderer or OS compositor.
+
+
@@ -2045,7 +2051,7 @@
The window background can be transparent.
- [b]Note:[/b] This flag has no effect if [member ProjectSettings.display/window/per_pixel_transparency/allowed] is set to [code]false[/code].
+ [b]Note:[/b] This flag has no effect if [method is_window_transparency_available] returns [code]false[/code].
[b]Note:[/b] Transparency support is implemented on Linux (X11/Wayland), macOS, and Windows, but availability might vary depending on GPU driver, display manager, and compositor capabilities.
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp
index 9407826ebfa..b042d415244 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.cpp
+++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp
@@ -2199,9 +2199,21 @@ Error RenderingDeviceDriverD3D12::swap_chain_resize(CommandQueueID p_cmd_queue,
swap_chain_desc.SampleDesc.Count = 1;
swap_chain_desc.Flags = creation_flags;
swap_chain_desc.Scaling = DXGI_SCALING_NONE;
+ if (OS::get_singleton()->is_layered_allowed()) {
+ swap_chain_desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
+ has_comp_alpha[(uint64_t)p_cmd_queue.id] = true;
+ } else {
+ swap_chain_desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
+ has_comp_alpha[(uint64_t)p_cmd_queue.id] = false;
+ }
ComPtr swap_chain_1;
res = context_driver->dxgi_factory_get()->CreateSwapChainForHwnd(command_queue->d3d_queue.Get(), surface->hwnd, &swap_chain_desc, nullptr, nullptr, swap_chain_1.GetAddressOf());
+ if (!SUCCEEDED(res) && swap_chain_desc.AlphaMode != DXGI_ALPHA_MODE_IGNORE) {
+ swap_chain_desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
+ has_comp_alpha[(uint64_t)p_cmd_queue.id] = false;
+ res = context_driver->dxgi_factory_get()->CreateSwapChainForHwnd(command_queue->d3d_queue.Get(), surface->hwnd, &swap_chain_desc, nullptr, nullptr, swap_chain_1.GetAddressOf());
+ }
ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE);
swap_chain_1.As(&swap_chain->d3d_swap_chain);
@@ -5980,6 +5992,13 @@ const RDD::Capabilities &RenderingDeviceDriverD3D12::get_capabilities() const {
return device_capabilities;
}
+bool RenderingDeviceDriverD3D12::is_composite_alpha_supported(CommandQueueID p_queue) const {
+ if (has_comp_alpha.has((uint64_t)p_queue.id)) {
+ return has_comp_alpha[(uint64_t)p_queue.id];
+ }
+ return false;
+}
+
/******************/
RenderingDeviceDriverD3D12::RenderingDeviceDriverD3D12(RenderingContextDriverD3D12 *p_context_driver) {
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.h b/drivers/d3d12/rendering_device_driver_d3d12.h
index 8e1223bdaad..3a9677485e5 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.h
+++ b/drivers/d3d12/rendering_device_driver_d3d12.h
@@ -973,6 +973,7 @@ private:
uint32_t frames_drawn = 0;
uint32_t segment_serial = 0;
bool segment_begun = false;
+ HashMap has_comp_alpha;
public:
virtual void begin_segment(uint32_t p_frame_index, uint32_t p_frames_drawn) override final;
@@ -994,6 +995,8 @@ public:
virtual String get_pipeline_cache_uuid() const override final;
virtual const Capabilities &get_capabilities() const override final;
+ virtual bool is_composite_alpha_supported(CommandQueueID p_queue) const override final;
+
static bool is_in_developer_mode();
private:
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp
index 896fc6ff91b..b03a8418ed9 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp
@@ -2646,6 +2646,7 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
break;
}
}
+ has_comp_alpha[(uint64_t)p_cmd_queue.id] = (composite_alpha != VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR);
}
VkSwapchainCreateInfoKHR swap_create_info = {};
@@ -4945,6 +4946,13 @@ const RDD::Capabilities &RenderingDeviceDriverVulkan::get_capabilities() const {
return device_capabilities;
}
+bool RenderingDeviceDriverVulkan::is_composite_alpha_supported(CommandQueueID p_queue) const {
+ if (has_comp_alpha.has((uint64_t)p_queue.id)) {
+ return has_comp_alpha[(uint64_t)p_queue.id];
+ }
+ return false;
+}
+
/******************/
RenderingDeviceDriverVulkan::RenderingDeviceDriverVulkan(RenderingContextDriverVulkan *p_context_driver) {
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h
index e70019962a8..b9e75630695 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.h
+++ b/drivers/vulkan/rendering_device_driver_vulkan.h
@@ -494,6 +494,7 @@ private:
static int caching_instance_count;
PipelineCache pipelines_cache;
String pipeline_cache_id;
+ HashMap has_comp_alpha;
public:
virtual void pipeline_free(PipelineID p_pipeline) override final;
@@ -627,6 +628,8 @@ public:
virtual String get_pipeline_cache_uuid() const override final;
virtual const Capabilities &get_capabilities() const override final;
+ virtual bool is_composite_alpha_supported(CommandQueueID p_queue) const override final;
+
private:
/*********************/
/**** BOOKKEEPING ****/
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index fb22cf5983b..2491064a580 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -5192,6 +5192,22 @@ void DisplayServerX11::set_context(Context p_context) {
}
}
+bool DisplayServerX11::is_window_transparency_available() const {
+ CharString net_wm_cm_name = vformat("_NET_WM_CM_S%d", XDefaultScreen(x11_display)).ascii();
+ Atom net_wm_cm = XInternAtom(x11_display, net_wm_cm_name.get_data(), False);
+ if (net_wm_cm == None) {
+ return false;
+ }
+ if (XGetSelectionOwner(x11_display, net_wm_cm) == None) {
+ return false;
+ }
+
+ if (rendering_device && !rendering_device->is_composite_alpha_supported()) {
+ return false;
+ }
+ return OS::get_singleton()->is_layered_allowed();
+}
+
void DisplayServerX11::set_native_icon(const String &p_filename) {
WARN_PRINT("Native icon not supported by this display server.");
}
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index 0861789b4a4..7c69df3df0d 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -530,6 +530,8 @@ public:
virtual void set_context(Context p_context) override;
+ virtual bool is_window_transparency_available() const override;
+
virtual void set_native_icon(const String &p_filename) override;
virtual void set_icon(const Ref &p_icon) override;
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index 3068ed071bf..608c34edc6f 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -439,6 +439,8 @@ public:
virtual Rect2 status_indicator_get_rect(IndicatorID p_id) const override;
virtual void delete_status_indicator(IndicatorID p_id) override;
+ virtual bool is_window_transparency_available() const override;
+
static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
static Vector get_rendering_drivers_func();
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 747de174394..0b11bf8287e 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -3314,6 +3314,13 @@ void DisplayServerMacOS::delete_status_indicator(IndicatorID p_id) {
indicators.erase(p_id);
}
+bool DisplayServerMacOS::is_window_transparency_available() const {
+ if (rendering_device && !rendering_device->is_composite_alpha_supported()) {
+ return false;
+ }
+ return OS::get_singleton()->is_layered_allowed();
+}
+
DisplayServer *DisplayServerMacOS::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
DisplayServer *ds = memnew(DisplayServerMacOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error));
if (r_error != OK) {
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 6ee35d4fbd2..b84e1f8eead 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -3401,6 +3401,19 @@ DisplayServer::VSyncMode DisplayServerWindows::window_get_vsync_mode(WindowID p_
void DisplayServerWindows::set_context(Context p_context) {
}
+bool DisplayServerWindows::is_window_transparency_available() const {
+ BOOL dwm_enabled = true;
+ if (DwmIsCompositionEnabled(&dwm_enabled) == S_OK) { // Note: Always enabled on Windows 8+, this check can be removed after Windows 7 support is dropped.
+ if (!dwm_enabled) {
+ return false;
+ }
+ }
+ if (rendering_device && !rendering_device->is_composite_alpha_supported()) {
+ return false;
+ }
+ return OS::get_singleton()->is_layered_allowed();
+}
+
#define MI_WP_SIGNATURE 0xFF515700
#define SIGNATURE_MASK 0xFFFFFF00
// Keeping the name suggested by Microsoft, but this macro really answers:
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index ef9c4701575..9a4eeba4864 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -697,6 +697,8 @@ public:
virtual void set_context(Context p_context) override;
+ virtual bool is_window_transparency_available() const override;
+
static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
static Vector get_rendering_drivers_func();
static void register_windows_driver();
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index 17f1548017e..2150a3038ee 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -996,6 +996,8 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("tablet_get_current_driver"), &DisplayServer::tablet_get_current_driver);
ClassDB::bind_method(D_METHOD("tablet_set_current_driver", "name"), &DisplayServer::tablet_set_current_driver);
+ ClassDB::bind_method(D_METHOD("is_window_transparency_available"), &DisplayServer::is_window_transparency_available);
+
#ifndef DISABLE_DEPRECATED
BIND_ENUM_CONSTANT(FEATURE_GLOBAL_MENU);
#endif
diff --git a/servers/display_server.h b/servers/display_server.h
index 9a9bb28a069..5224d59c04a 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -580,6 +580,8 @@ public:
virtual void set_context(Context p_context);
+ virtual bool is_window_transparency_available() const { return false; }
+
static void register_create_function(const char *p_name, CreateFunction p_function, GetRenderingDriversFunction p_get_drivers);
static int get_create_function_count();
static const char *get_create_function_name(int p_index);
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index 15e1731823f..98dc9885fc5 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -4734,6 +4734,10 @@ String RenderingDevice::get_device_api_name() const {
return driver->get_api_name();
}
+bool RenderingDevice::is_composite_alpha_supported() const {
+ return driver->is_composite_alpha_supported(main_queue);
+}
+
String RenderingDevice::get_device_api_version() const {
return driver->get_api_version();
}
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index 42773fc3471..d0fa4ab1fa5 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -1360,6 +1360,8 @@ public:
String get_device_api_version() const;
String get_device_pipeline_cache_uuid() const;
+ bool is_composite_alpha_supported() const;
+
uint64_t get_driver_resource(DriverResource p_resource, RID p_rid = RID(), uint64_t p_index = 0);
static RenderingDevice *get_singleton();
diff --git a/servers/rendering/rendering_device_driver.h b/servers/rendering/rendering_device_driver.h
index e9464ba3218..f9a861426ad 100644
--- a/servers/rendering/rendering_device_driver.h
+++ b/servers/rendering/rendering_device_driver.h
@@ -769,6 +769,8 @@ public:
virtual String get_pipeline_cache_uuid() const = 0;
virtual const Capabilities &get_capabilities() const = 0;
+ virtual bool is_composite_alpha_supported(CommandQueueID p_queue) const { return false; }
+
/******************/
virtual ~RenderingDeviceDriver();