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();