From d5504ffc696dd9d97694741355e9b9d8b5821742 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Sat, 7 Feb 2026 10:52:07 +1100 Subject: [PATCH] feat: Add EDR support to Metal and all Apple platforms --- doc/classes/DisplayServer.xml | 2 +- .../display_server_apple_embedded.h | 28 ++ .../display_server_apple_embedded.mm | 101 ++++++ .../godot_view_apple_embedded.mm | 16 + .../metal/rendering_context_driver_metal.cpp | 39 ++- .../metal/rendering_context_driver_metal.h | 20 +- .../metal/rendering_device_driver_metal.cpp | 48 ++- drivers/metal/rendering_device_driver_metal.h | 6 +- .../vulkan/rendering_device_driver_vulkan.cpp | 3 + platform/ios/display_server_ios.h | 7 +- platform/ios/display_server_ios.mm | 23 ++ platform/macos/SCsub | 2 +- platform/macos/display_server_macos.h | 52 +--- platform/macos/display_server_macos.mm | 212 +++++-------- platform/macos/display_server_macos_base.h | 110 ++++++- platform/macos/display_server_macos_base.mm | 285 +++++++++++++++++ ...dded.h => display_server_macos_embedded.h} | 66 ++-- ...ed.mm => display_server_macos_embedded.mm} | 288 ++++++++---------- .../macos/editor/embedded_process_macos.mm | 9 +- platform/macos/embedded_debugger.h | 8 +- platform/macos/embedded_debugger.mm | 8 +- platform/macos/os_macos.mm | 8 +- platform/visionos/display_server_visionos.h | 5 + platform/visionos/display_server_visionos.mm | 16 + 24 files changed, 912 insertions(+), 450 deletions(-) rename platform/macos/{display_server_embedded.h => display_server_macos_embedded.h} (83%) rename platform/macos/{display_server_embedded.mm => display_server_macos_embedded.mm} (66%) diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 1cbef2af3d..3b16d95b69 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -2751,7 +2751,7 @@ Display server supports interaction with screen reader or Braille display. [b]Linux (X11/Wayland), macOS, Windows[/b] - Display server supports HDR output. [b]Windows[/b] + Display server supports HDR output. [b]macOS, iOS, visionOS, Windows[/b] Unknown or custom role. diff --git a/drivers/apple_embedded/display_server_apple_embedded.h b/drivers/apple_embedded/display_server_apple_embedded.h index 36c5fd9cee..3bb6b8a45f 100644 --- a/drivers/apple_embedded/display_server_apple_embedded.h +++ b/drivers/apple_embedded/display_server_apple_embedded.h @@ -57,6 +57,7 @@ class InputEvent; class NativeMenu; +/// "Embedded" as in "Embedded Device". class DisplayServerAppleEmbedded : public DisplayServer { GDSOFTCLASS(DisplayServerAppleEmbedded, DisplayServer); @@ -87,7 +88,16 @@ class DisplayServerAppleEmbedded : public DisplayServer { void initialize_tts() const; + bool edr_requested = false; + void _update_hdr_output(); + float _calculate_current_reference_luminance() const; + protected: + virtual bool _screen_hdr_is_supported() const { return false; } + virtual float _screen_potential_edr_headroom() const { return 1.0f; } + virtual float _screen_current_edr_headroom() const { return 1.0f; } + float hardware_reference_luminance_nits = 100.0f; + DisplayServerAppleEmbedded(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error); ~DisplayServerAppleEmbedded(); @@ -230,6 +240,24 @@ public: virtual void screen_set_keep_on(bool p_enable) override; virtual bool screen_is_kept_on() const override; + // MARK: - HDR / EDR + + void current_edr_headroom_changed(); + virtual bool window_is_hdr_output_supported(WindowID p_window = MAIN_WINDOW_ID) const override; + virtual void window_request_hdr_output(const bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override; + virtual bool window_is_hdr_output_requested(WindowID p_window = MAIN_WINDOW_ID) const override; + virtual bool window_is_hdr_output_enabled(WindowID p_window = MAIN_WINDOW_ID) const override; + + virtual void window_set_hdr_output_reference_luminance(const float p_reference_luminance, WindowID p_window = MAIN_WINDOW_ID) override; + virtual float window_get_hdr_output_reference_luminance(WindowID p_window = MAIN_WINDOW_ID) const override; + virtual float window_get_hdr_output_current_reference_luminance(WindowID p_window = MAIN_WINDOW_ID) const override; + + virtual void window_set_hdr_output_max_luminance(const float p_max_luminance, WindowID p_window = MAIN_WINDOW_ID) override; + virtual float window_get_hdr_output_max_luminance(WindowID p_window = MAIN_WINDOW_ID) const override; + virtual float window_get_hdr_output_current_max_luminance(WindowID p_window = MAIN_WINDOW_ID) const override; + + virtual float window_get_output_max_linear_value(WindowID p_window = MAIN_WINDOW_ID) const override; + void resize_window(CGSize size); virtual void swap_buffers() override {} diff --git a/drivers/apple_embedded/display_server_apple_embedded.mm b/drivers/apple_embedded/display_server_apple_embedded.mm index ac155b153f..b21f88d73b 100644 --- a/drivers/apple_embedded/display_server_apple_embedded.mm +++ b/drivers/apple_embedded/display_server_apple_embedded.mm @@ -376,6 +376,7 @@ bool DisplayServerAppleEmbedded::has_feature(Feature p_feature) const { // case FEATURE_NATIVE_ICON: // case FEATURE_WINDOW_TRANSPARENCY: case FEATURE_CLIPBOARD: + case FEATURE_HDR_OUTPUT: case FEATURE_KEEP_SCREEN_ON: case FEATURE_ORIENTATION: case FEATURE_TOUCHSCREEN: @@ -820,6 +821,106 @@ DisplayServer::VSyncMode DisplayServerAppleEmbedded::window_get_vsync_mode(Windo return DisplayServer::VSYNC_ENABLED; } +// MARK: - HDR / EDR + +void DisplayServerAppleEmbedded::_update_hdr_output() { +#ifdef RD_ENABLED + if (!rendering_context) { + return; + } + + bool desired = edr_requested && _screen_hdr_is_supported(); + if (rendering_context->window_get_hdr_output_enabled(MAIN_WINDOW_ID) != desired) { + rendering_context->window_set_hdr_output_enabled(MAIN_WINDOW_ID, desired); + } + + float reference_luminance = _calculate_current_reference_luminance(); + rendering_context->window_set_hdr_output_reference_luminance(MAIN_WINDOW_ID, reference_luminance); + + float max_luminance = _screen_potential_edr_headroom() * hardware_reference_luminance_nits; + rendering_context->window_set_hdr_output_max_luminance(MAIN_WINDOW_ID, max_luminance); +#endif +} + +void DisplayServerAppleEmbedded::current_edr_headroom_changed() { + _update_hdr_output(); +} + +bool DisplayServerAppleEmbedded::window_is_hdr_output_supported(WindowID p_window) const { +#if defined(RD_ENABLED) + if (rendering_device && !rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT)) { + return false; + } +#endif + return _screen_hdr_is_supported(); +} + +void DisplayServerAppleEmbedded::window_request_hdr_output(const bool p_enabled, WindowID p_window) { +#if defined(RD_ENABLED) + ERR_FAIL_COND_MSG(p_enabled && rendering_device && !rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT), "HDR output is not supported by the rendering device."); +#endif + + edr_requested = p_enabled; + _update_hdr_output(); +} + +bool DisplayServerAppleEmbedded::window_is_hdr_output_requested(WindowID p_window) const { + return edr_requested; +} + +bool DisplayServerAppleEmbedded::window_is_hdr_output_enabled(WindowID p_window) const { +#if defined(RD_ENABLED) + if (rendering_context) { + return rendering_context->window_get_hdr_output_enabled(p_window); + } +#endif + return false; +} + +void DisplayServerAppleEmbedded::window_set_hdr_output_reference_luminance(const float p_reference_luminance, WindowID p_window) { + ERR_PRINT_ONCE("Manually setting reference white luminance is not supported on Apple devices, as they provide a user-facing brightness setting that directly controls reference white luminance."); +} + +float DisplayServerAppleEmbedded::window_get_hdr_output_reference_luminance(WindowID p_window) const { + return -1.0f; // Always auto-adjusted by the OS on Apple platforms. +} + +float DisplayServerAppleEmbedded::_calculate_current_reference_luminance() const { + float potential = _screen_potential_edr_headroom(); + float current = _screen_current_edr_headroom(); + return potential * hardware_reference_luminance_nits / current; +} + +float DisplayServerAppleEmbedded::window_get_hdr_output_current_reference_luminance(WindowID p_window) const { +#if defined(RD_ENABLED) + if (rendering_context) { + return rendering_context->window_get_hdr_output_reference_luminance(p_window); + } +#endif + return 200.0f; +} + +void DisplayServerAppleEmbedded::window_set_hdr_output_max_luminance(const float p_max_luminance, WindowID p_window) { + ERR_PRINT_ONCE("Manually setting max luminance is not supported on Apple embedded devices as they provide accurate max luminance values for their built-in screens."); +} + +float DisplayServerAppleEmbedded::window_get_hdr_output_max_luminance(WindowID p_window) const { + return -1.0f; +} + +float DisplayServerAppleEmbedded::window_get_hdr_output_current_max_luminance(WindowID p_window) const { + return _screen_potential_edr_headroom() * hardware_reference_luminance_nits; +} + +float DisplayServerAppleEmbedded::window_get_output_max_linear_value(WindowID p_window) const { +#if defined(RD_ENABLED) + if (rendering_context) { + return rendering_context->window_get_output_max_linear_value(p_window); + } +#endif + return 1.0f; +} + void DisplayServerAppleEmbedded::set_native_icon(const String &p_filename) { // Not supported on Apple embedded platforms. } diff --git a/drivers/apple_embedded/godot_view_apple_embedded.mm b/drivers/apple_embedded/godot_view_apple_embedded.mm index 8f187703a2..4c94459c92 100644 --- a/drivers/apple_embedded/godot_view_apple_embedded.mm +++ b/drivers/apple_embedded/godot_view_apple_embedded.mm @@ -45,6 +45,7 @@ static const float earth_gravity = 9.80665; @interface GDTView () { UITouch *godot_touches[max_touches]; + CGFloat last_edr_headroom; } @property(assign, nonatomic) BOOL isActive; @@ -121,6 +122,7 @@ static const float earth_gravity = 9.80665; - (void)godot_commonInit { self.preferredFrameRate = 60; self.useCADisplayLink = bool(GLOBAL_DEF("display.AppleEmbedded/use_cadisplaylink", true)) ? YES : NO; + last_edr_headroom = 0.0; #if !defined(VISIONOS_ENABLED) self.contentScaleFactor = [UIScreen mainScreen].scale; @@ -249,6 +251,20 @@ static const float earth_gravity = 9.80665; } [self handleMotion]; + +#if !defined(VISIONOS_ENABLED) + if (@available(iOS 16.0, *)) { + CGFloat edr_headroom = UIScreen.mainScreen.currentEDRHeadroom; + if (last_edr_headroom != edr_headroom) { + last_edr_headroom = edr_headroom; + if (DisplayServerAppleEmbedded::get_singleton()) { + DisplayServerAppleEmbedded::get_singleton()->current_edr_headroom_changed(); + } + } + } + +#endif + [self.renderer renderOnView:self]; [self.renderingLayer stopRenderDisplayLayer]; diff --git a/drivers/metal/rendering_context_driver_metal.cpp b/drivers/metal/rendering_context_driver_metal.cpp index 610fd4c45a..b1a2c436c5 100644 --- a/drivers/metal/rendering_context_driver_metal.cpp +++ b/drivers/metal/rendering_context_driver_metal.cpp @@ -119,7 +119,7 @@ public: layer = nullptr; } - Error resize(uint32_t p_desired_framebuffer_count) override final { + Error resize(uint32_t p_desired_framebuffer_count, RDD::DataFormat &r_format, RDD::ColorSpace &r_color_space) override final { if (width == 0 || height == 0) { // Very likely the window is minimized, don't create a swap chain. return ERR_SKIP; @@ -155,6 +155,28 @@ public: frame_buffers[i].set_texture_count(1); } + if (hdr_output) { + layer->setWantsExtendedDynamicRangeContent(true); + CGColorSpaceRef color_space = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB); + layer->setColorspace(color_space); + CGColorSpaceRelease(color_space); + layer->setPixelFormat(MTL::PixelFormatRGBA16Float); + + r_color_space = RDD::COLOR_SPACE_REC709_LINEAR; + r_format = RDD::DATA_FORMAT_R16G16B16A16_SFLOAT; + pixel_format = MTL::PixelFormatRGBA16Float; + } else { + layer->setWantsExtendedDynamicRangeContent(false); + CGColorSpaceRef color_space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + layer->setColorspace(color_space); + CGColorSpaceRelease(color_space); + layer->setPixelFormat(MTL::PixelFormatBGRA8Unorm); + + r_color_space = RDD::COLOR_SPACE_REC709_NONLINEAR_SRGB; + r_format = RDD::DATA_FORMAT_B8G8R8A8_UNORM; + pixel_format = MTL::PixelFormatBGRA8Unorm; + } + return OK; } @@ -256,7 +278,7 @@ public: memdelete_arr(frame_buffers); } - Error resize(uint32_t p_desired_framebuffer_count) override final { + Error resize(uint32_t p_desired_framebuffer_count, RDD::DataFormat &r_format, RDD::ColorSpace &r_color_space) override final { if (width == 0 || height == 0) { // Very likely the window is minimized, don't create a swap chain. return ERR_SKIP; @@ -379,13 +401,12 @@ DisplayServer::VSyncMode RenderingContextDriverMetal::surface_get_vsync_mode(Sur void RenderingContextDriverMetal::surface_set_hdr_output_enabled(SurfaceID p_surface, bool p_enabled) { Surface *surface = (Surface *)(p_surface); - surface->hdr_output = p_enabled; - surface->needs_resize = true; + surface->set_hdr_output_enabled(p_enabled); } bool RenderingContextDriverMetal::surface_get_hdr_output_enabled(SurfaceID p_surface) const { Surface *surface = (Surface *)(p_surface); - return surface->hdr_output; + return surface->is_hdr_output_enabled(); } void RenderingContextDriverMetal::surface_set_hdr_output_reference_luminance(SurfaceID p_surface, float p_reference_luminance) { @@ -394,7 +415,7 @@ void RenderingContextDriverMetal::surface_set_hdr_output_reference_luminance(Sur } float RenderingContextDriverMetal::surface_get_hdr_output_reference_luminance(SurfaceID p_surface) const { - Surface *surface = (Surface *)(p_surface); + const Surface *surface = (Surface *)(p_surface); return surface->hdr_reference_luminance; } @@ -404,18 +425,16 @@ void RenderingContextDriverMetal::surface_set_hdr_output_max_luminance(SurfaceID } float RenderingContextDriverMetal::surface_get_hdr_output_max_luminance(SurfaceID p_surface) const { - Surface *surface = (Surface *)(p_surface); + const Surface *surface = (Surface *)(p_surface); return surface->hdr_max_luminance; } void RenderingContextDriverMetal::surface_set_hdr_output_linear_luminance_scale(SurfaceID p_surface, float p_linear_luminance_scale) { - Surface *surface = (Surface *)(p_surface); - surface->hdr_linear_luminance_scale = p_linear_luminance_scale; } float RenderingContextDriverMetal::surface_get_hdr_output_linear_luminance_scale(SurfaceID p_surface) const { Surface *surface = (Surface *)(p_surface); - return surface->hdr_linear_luminance_scale; + return surface->hdr_reference_luminance; } float RenderingContextDriverMetal::surface_get_hdr_output_max_value(SurfaceID p_surface) const { diff --git a/drivers/metal/rendering_context_driver_metal.h b/drivers/metal/rendering_context_driver_metal.h index b54303c6f9..d33a87b5d6 100644 --- a/drivers/metal/rendering_context_driver_metal.h +++ b/drivers/metal/rendering_context_driver_metal.h @@ -91,22 +91,32 @@ public: uint32_t width = 0; uint32_t height = 0; DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED; - bool needs_resize = false; double present_minimum_duration = 0.0; + MTL::PixelFormat pixel_format = MTL::PixelFormatBGRA8Unorm; - bool hdr_output = false; // BT.2408 recommendation of 203 nits for HDR Reference White, rounded to 200 // to be a more pleasant player-facing value. float hdr_reference_luminance = 200.0f; float hdr_max_luminance = 1000.0f; - float hdr_linear_luminance_scale = 100.0f; + bool needs_resize = false; + bool hdr_output = false; Surface(MTL::Device *p_device) : device(p_device) {} virtual ~Surface() = default; - MTL::PixelFormat get_pixel_format() const { return MTL::PixelFormatBGRA8Unorm; } - virtual Error resize(uint32_t p_desired_framebuffer_count) = 0; + MTL::PixelFormat get_pixel_format() const { return pixel_format; } + void set_hdr_output_enabled(bool p_enabled) { + if (hdr_output != p_enabled) { + hdr_output = p_enabled; + needs_resize = true; + } + } + + bool is_hdr_output_enabled() const { + return hdr_output; + } + virtual Error resize(uint32_t p_desired_framebuffer_count, RDD::DataFormat &r_format, RDD::ColorSpace &r_color_space) = 0; virtual RDD::FramebufferID acquire_next_frame_buffer() = 0; virtual void present(MTL3::MDCommandBuffer *p_cmd_buffer) = 0; virtual MTL::Drawable *next_drawable() = 0; diff --git a/drivers/metal/rendering_device_driver_metal.cpp b/drivers/metal/rendering_device_driver_metal.cpp index 2943c15b07..61fdcfcd0b 100644 --- a/drivers/metal/rendering_device_driver_metal.cpp +++ b/drivers/metal/rendering_device_driver_metal.cpp @@ -881,6 +881,11 @@ void RenderingDeviceDriverMetal::command_buffer_execute_secondary(CommandBufferI void RenderingDeviceDriverMetal::_swap_chain_release(SwapChain *p_swap_chain) { _swap_chain_release_buffers(p_swap_chain); + + if (p_swap_chain->render_pass.id != 0) { + render_pass_free(p_swap_chain->render_pass); + p_swap_chain->render_pass = RenderPassID(); + } } void RenderingDeviceDriverMetal::_swap_chain_release_buffers(SwapChain *p_swap_chain) { @@ -894,9 +899,15 @@ RDD::SwapChainID RenderingDeviceDriverMetal::swap_chain_create(RenderingContextD GODOT_CLANG_WARNING_POP } + SwapChain *swap_chain = memnew(SwapChain); + swap_chain->surface = p_surface; + return SwapChainID(swap_chain); +} + +RDD::RenderPassID RenderingDeviceDriverMetal::_swap_chain_create_render_pass(RDD::DataFormat p_format) { // Create the render pass that will be used to draw to the swap chain's framebuffers. RDD::Attachment attachment; - attachment.format = pixel_formats->getDataFormat(surface->get_pixel_format()); + attachment.format = p_format; attachment.samples = RDD::TEXTURE_SAMPLES_1; attachment.load_op = RDD::ATTACHMENT_LOAD_OP_CLEAR; attachment.store_op = RDD::ATTACHMENT_STORE_OP_STORE; @@ -907,15 +918,7 @@ RDD::SwapChainID RenderingDeviceDriverMetal::swap_chain_create(RenderingContextD color_ref.aspect.set_flag(RDD::TEXTURE_ASPECT_COLOR_BIT); subpass.color_references.push_back(color_ref); - RenderPassID render_pass = render_pass_create(attachment, subpass, {}, 1, RDD::AttachmentReference()); - ERR_FAIL_COND_V(!render_pass, SwapChainID()); - - // Create the empty swap chain until it is resized. - SwapChain *swap_chain = memnew(SwapChain); - swap_chain->surface = p_surface; - swap_chain->data_format = attachment.format; - swap_chain->render_pass = render_pass; - return SwapChainID(swap_chain); + return render_pass_create(attachment, subpass, {}, 1, RDD::AttachmentReference()); } Error RenderingDeviceDriverMetal::swap_chain_resize(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, uint32_t p_desired_framebuffer_count) { @@ -924,7 +927,24 @@ Error RenderingDeviceDriverMetal::swap_chain_resize(CommandQueueID p_cmd_queue, SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id); RenderingContextDriverMetal::Surface *surface = (RenderingContextDriverMetal::Surface *)(swap_chain->surface); - surface->resize(p_desired_framebuffer_count); + + DataFormat new_data_format = DATA_FORMAT_MAX; + ColorSpace new_color_space = COLOR_SPACE_MAX; + Error err = surface->resize(p_desired_framebuffer_count, new_data_format, new_color_space); + if (err != OK) { + return err; + } + + if (new_data_format != swap_chain->data_format) { + _swap_chain_release(swap_chain); + swap_chain->render_pass = _swap_chain_create_render_pass(new_data_format); + if (!swap_chain->render_pass) { + return ERR_INVALID_DATA; + } + } + + swap_chain->data_format = new_data_format; + swap_chain->color_space = new_color_space; // Once everything's been created correctly, indicate the surface no longer needs to be resized. context_driver->surface_set_needs_resize(swap_chain->surface, false); @@ -957,7 +977,8 @@ RDD::DataFormat RenderingDeviceDriverMetal::swap_chain_get_format(SwapChainID p_ } RDD::ColorSpace RenderingDeviceDriverMetal::swap_chain_get_color_space(SwapChainID p_swap_chain) { - return RDD::COLOR_SPACE_REC709_NONLINEAR_SRGB; + const SwapChain *swap_chain = (const SwapChain *)(p_swap_chain.id); + return swap_chain->color_space; } void RenderingDeviceDriverMetal::swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) { @@ -975,7 +996,6 @@ void RenderingDeviceDriverMetal::swap_chain_free(SwapChainID p_swap_chain) { GODOT_CLANG_WARNING_POP } _swap_chain_release(swap_chain); - render_pass_free(swap_chain->render_pass); memdelete(swap_chain); } @@ -2645,6 +2665,8 @@ bool RenderingDeviceDriverMetal::has_feature(Features p_feature) { return device_properties->features.metal_fx_spatial; case SUPPORTS_METALFX_TEMPORAL: return device_properties->features.metal_fx_temporal; + case SUPPORTS_HDR_OUTPUT: + return true; case SUPPORTS_IMAGE_ATOMIC_32_BIT: return device_properties->features.supports_native_image_atomics; case SUPPORTS_VULKAN_MEMORY_MODEL: diff --git a/drivers/metal/rendering_device_driver_metal.h b/drivers/metal/rendering_device_driver_metal.h index e4d99abc92..6904b1d124 100644 --- a/drivers/metal/rendering_device_driver_metal.h +++ b/drivers/metal/rendering_device_driver_metal.h @@ -301,11 +301,11 @@ protected: struct SwapChain { RenderingContextDriver::SurfaceID surface = RenderingContextDriver::SurfaceID(); RenderPassID render_pass; - RDD::DataFormat data_format = DATA_FORMAT_MAX; - SwapChain() : - render_pass(nullptr) {} + DataFormat data_format = DATA_FORMAT_MAX; + ColorSpace color_space = COLOR_SPACE_MAX; }; + RenderPassID _swap_chain_create_render_pass(DataFormat p_format); void _swap_chain_release(SwapChain *p_swap_chain); void _swap_chain_release_buffers(SwapChain *p_swap_chain); diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp index 5c999af94b..13b9b40cb9 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -7327,6 +7327,9 @@ bool RenderingDeviceDriverVulkan::has_feature(Features p_feature) { // involving integrated GPU hardware do not function correctly // with HDR output. return false; +#elif defined(MACOS_ENABLED) || defined(APPLE_EMBEDDED_ENABLED) + // HDR support has not yet been thoroughly tested and validated for Apple platforms. + return false; #else return context_driver->is_colorspace_supported(); #endif // defined(WINDOWS_ENABLED) diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h index c8538d29e2..e196e79471 100644 --- a/platform/ios/display_server_ios.h +++ b/platform/ios/display_server_ios.h @@ -32,7 +32,7 @@ #include "drivers/apple_embedded/display_server_apple_embedded.h" -class DisplayServerIOS : public DisplayServerAppleEmbedded { +class DisplayServerIOS final : public DisplayServerAppleEmbedded { GDSOFTCLASS(DisplayServerIOS, DisplayServerAppleEmbedded); _THREAD_SAFE_CLASS_ @@ -51,4 +51,9 @@ public: virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; + +protected: + virtual bool _screen_hdr_is_supported() const override; + virtual float _screen_potential_edr_headroom() const override; + virtual float _screen_current_edr_headroom() const override; }; diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index fa42f8b3a2..ae3f613bdc 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -41,6 +41,8 @@ DisplayServerIOS *DisplayServerIOS::get_singleton() { DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) : DisplayServerAppleEmbedded(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, p_parent_window, r_error) { + // See: https://github.com/godotengine/godot/pull/106814#discussion_r2854262156 for why this was set to 203 nits. + hardware_reference_luminance_nits = 203.0f; } DisplayServerIOS::~DisplayServerIOS() { @@ -118,3 +120,24 @@ float DisplayServerIOS::screen_get_scale(int p_screen) const { return [UIScreen mainScreen].scale; } + +bool DisplayServerIOS::_screen_hdr_is_supported() const { + if (@available(iOS 16.0, *)) { + return [UIScreen mainScreen].potentialEDRHeadroom > 1.0; + } + return false; +} + +float DisplayServerIOS::_screen_potential_edr_headroom() const { + if (@available(iOS 16.0, *)) { + return [UIScreen mainScreen].potentialEDRHeadroom; + } + return 1.0f; +} + +float DisplayServerIOS::_screen_current_edr_headroom() const { + if (@available(iOS 16.0, *)) { + return [UIScreen mainScreen].currentEDRHeadroom; + } + return 1.0f; +} diff --git a/platform/macos/SCsub b/platform/macos/SCsub index e2c15aba06..a8181707ef 100644 --- a/platform/macos/SCsub +++ b/platform/macos/SCsub @@ -33,7 +33,7 @@ files = [ if env.editor_build: files += [ - "display_server_embedded.mm", + "display_server_macos_embedded.mm", "embedded_debugger.mm", "embedded_gl_manager.mm", "editor/embedded_game_view_plugin.mm", diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index 12ba7efe76..b030858370 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -151,6 +151,8 @@ public: bool extend_to_title = false; bool hide_from_capture = false; + HDROutput hdr_output; + Rect2i parent_safe_rect; }; @@ -160,13 +162,11 @@ public: GodotProgressView *dock_progress = nullptr; private: + id screen_observer = nil; + #if defined(GLES3_ENABLED) GLManagerLegacy_MacOS *gl_manager_legacy = nullptr; GLManagerANGLE_MacOS *gl_manager_angle = nullptr; -#endif -#if defined(RD_ENABLED) - RenderingContextDriver *rendering_context = nullptr; - RenderingDevice *rendering_device = nullptr; #endif String rendering_driver; @@ -184,15 +184,11 @@ private: id menu_delegate = nullptr; NativeMenuMacOS *native_menu = nullptr; - Point2i im_selection; - String im_text; - CGEventSourceRef event_source; - MouseMode mouse_mode = MOUSE_MODE_VISIBLE; - MouseMode mouse_mode_base = MOUSE_MODE_VISIBLE; - MouseMode mouse_mode_override = MOUSE_MODE_VISIBLE; - bool mouse_mode_override_enabled = false; - void _mouse_update_mode(); + void _mouse_apply_mode(MouseMode p_prev_mode, MouseMode p_new_mode) override; + + HDROutput &_get_hdr_output(WindowID p_window) override; + const HDROutput &_get_hdr_output(WindowID p_window) const override; bool drop_events = false; bool in_dispatch_input_event = false; @@ -205,7 +201,6 @@ private: mutable bool displays_arrangement_dirty = true; bool is_resizing = false; - CursorShape cursor_shape = CURSOR_ARROW; NSCursor *cursors[CURSOR_MAX]; HashMap> cursors_cache; @@ -219,8 +214,6 @@ private: IndicatorID indicator_id_counter = 0; HashMap indicators; - IOPMAssertionID screen_keep_on_assertion = kIOPMNullAssertionID; - Callable help_search_callback; Callable help_action_callback; @@ -275,7 +268,6 @@ public: void update_mouse_pos(WindowData &p_wd, NSPoint p_location_in_window); void push_to_key_event_buffer(const KeyEvent &p_event); void pop_last_key_event(); - void update_im_text(const Point2i &p_selection, const String &p_text); void set_last_focused_window(WindowID p_window); bool mouse_process_popups(bool p_close = false); void popup_open(WindowID p_window); @@ -312,22 +304,12 @@ public: virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector &p_filters, const Callable &p_callback, WindowID p_window_id) override; virtual Error file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector &p_filters, const TypedArray &p_options, const Callable &p_callback, WindowID p_window_id) override; - virtual void beep() const override; - - virtual void mouse_set_mode(MouseMode p_mode) override; - virtual MouseMode mouse_get_mode() const override; - virtual void mouse_set_mode_override(MouseMode p_mode) override; - virtual MouseMode mouse_get_mode_override() const override; - virtual void mouse_set_mode_override_enabled(bool p_override_enabled) override; - virtual bool mouse_is_mode_override_enabled() const override; - bool update_mouse_wrap(WindowData &p_wd, NSPoint &r_delta, NSPoint &r_mpos, NSTimeInterval p_timestamp); virtual void warp_mouse(const Point2i &p_position) override; virtual Point2i mouse_get_position() const override; virtual BitField mouse_get_button_state() const override; virtual int get_screen_count() const override; - virtual int get_primary_screen() const override; virtual int get_keyboard_focus_screen() const override; virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; @@ -335,13 +317,9 @@ public: virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual float screen_get_max_scale() const override; virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; - virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual Color screen_get_pixel(const Point2i &p_position) const override; virtual Ref screen_get_image(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual Ref screen_get_image_rect(const Rect2i &p_rect) const override; - virtual void screen_set_keep_on(bool p_enable) override; - virtual bool screen_is_kept_on() const override; - virtual Vector get_window_list() const override; virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i(), bool p_exclusive = false, WindowID p_transient_parent = INVALID_WINDOW_ID) override; @@ -416,6 +394,9 @@ public: virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override; virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override; +public: + void update_screen_parameters(); + virtual bool window_maximize_on_title_dbl_click() const override; virtual bool window_minimize_on_title_dbl_click() const override; @@ -425,17 +406,8 @@ public: virtual void window_set_window_buttons_offset(const Vector2i &p_offset, WindowID p_window = MAIN_WINDOW_ID) override; virtual Vector3i window_get_safe_title_margins(WindowID p_window = MAIN_WINDOW_ID) const override; - virtual int accessibility_should_increase_contrast() const override; - virtual int accessibility_should_reduce_animation() const override; - virtual int accessibility_should_reduce_transparency() const override; - virtual int accessibility_screen_reader_active() const override; - - virtual Point2i ime_get_selection() const override; - virtual String ime_get_text() const override; - void cursor_update_shape(); virtual void cursor_set_shape(CursorShape p_shape) override; - virtual CursorShape cursor_get_shape() const override; virtual void cursor_set_custom_image(const Ref &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override; virtual bool get_swap_cancel_ok() override; @@ -467,6 +439,8 @@ public: virtual bool is_window_transparency_available() const override; + void window_get_edr_values(WindowID p_window, CGFloat *r_max_potential_edr_value, CGFloat *r_max_edr_value) 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, int64_t p_parent_window, 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 d75eae0adf..98d5e15251 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -63,7 +63,7 @@ #include "servers/rendering/dummy/rasterizer_dummy.h" #ifdef TOOLS_ENABLED -#import "display_server_embedded.h" +#import "display_server_macos_embedded.h" #import "editor/embedded_process_macos.h" #endif @@ -742,13 +742,6 @@ void DisplayServerMacOS::push_to_key_event_buffer(const DisplayServerMacOS::KeyE key_event_buffer.write[key_event_pos++] = p_event; } -void DisplayServerMacOS::update_im_text(const Point2i &p_selection, const String &p_text) { - im_selection = p_selection; - im_text = p_text; - - OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE); -} - void DisplayServerMacOS::set_last_focused_window(WindowID p_window) { last_focused_window = p_window; } @@ -842,6 +835,7 @@ bool DisplayServerMacOS::has_feature(Feature p_feature) const { case FEATURE_SCREEN_EXCLUDE_FROM_CAPTURE: case FEATURE_EMOJI_AND_SYMBOL_PICKER: case FEATURE_WINDOW_EMBEDDING: + case FEATURE_HDR_OUTPUT: return true; #ifdef ACCESSKIT_ENABLED case FEATURE_ACCESSIBILITY_SCREEN_READER: { @@ -1174,10 +1168,6 @@ Error DisplayServerMacOS::_file_dialog_with_options_show(const String &p_title, return OK; } -void DisplayServerMacOS::beep() const { - NSBeep(); -} - Error DisplayServerMacOS::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) { _THREAD_SAFE_METHOD_ @@ -1214,32 +1204,24 @@ Error DisplayServerMacOS::dialog_input_text(String p_title, String p_description return OK; } -void DisplayServerMacOS::_mouse_update_mode() { +void DisplayServerMacOS::_mouse_apply_mode(MouseMode p_prev_mode, MouseMode p_new_mode) { _THREAD_SAFE_METHOD_ - MouseMode wanted_mouse_mode = mouse_mode_override_enabled - ? mouse_mode_override - : mouse_mode_base; - - if (wanted_mouse_mode == mouse_mode) { - return; - } - WindowID window_id = _get_focused_window_or_popup(); if (!windows.has(window_id)) { window_id = MAIN_WINDOW_ID; } WindowData &wd = windows[window_id]; - bool show_cursor = (wanted_mouse_mode == MOUSE_MODE_VISIBLE || wanted_mouse_mode == MOUSE_MODE_CONFINED); - bool previously_shown = (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED); + bool show_cursor = (p_new_mode == MOUSE_MODE_VISIBLE || p_new_mode == MOUSE_MODE_CONFINED); + bool previously_shown = (p_prev_mode == MOUSE_MODE_VISIBLE || p_prev_mode == MOUSE_MODE_CONFINED); if (show_cursor && !previously_shown) { window_id = get_window_at_screen_position(mouse_get_position()); mouse_enter_window(window_id); } - if (wanted_mouse_mode == MOUSE_MODE_CAPTURED) { + if (p_new_mode == MOUSE_MODE_CAPTURED) { // Apple Docs state that the display parameter is not used. // "This parameter is not used. By default, you may pass kCGDirectMainDisplay." // https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/Quartz_Services_Ref/Reference/reference.html @@ -1253,17 +1235,17 @@ void DisplayServerMacOS::_mouse_update_mode() { NSPoint pointOnScreen = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin; CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y }; CGWarpMouseCursorPosition(lMouseWarpPos); - } else if (wanted_mouse_mode == MOUSE_MODE_HIDDEN) { + } else if (p_new_mode == MOUSE_MODE_HIDDEN) { if (previously_shown) { CGDisplayHideCursor(kCGDirectMainDisplay); } [wd.window_object setMovable:YES]; CGAssociateMouseAndMouseCursorPosition(true); - } else if (wanted_mouse_mode == MOUSE_MODE_CONFINED) { + } else if (p_new_mode == MOUSE_MODE_CONFINED) { CGDisplayShowCursor(kCGDirectMainDisplay); [wd.window_object setMovable:NO]; CGAssociateMouseAndMouseCursorPosition(false); - } else if (wanted_mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { + } else if (p_new_mode == MOUSE_MODE_CONFINED_HIDDEN) { if (previously_shown) { CGDisplayHideCursor(kCGDirectMainDisplay); } @@ -1278,51 +1260,12 @@ void DisplayServerMacOS::_mouse_update_mode() { last_warp = [[NSProcessInfo processInfo] systemUptime]; ignore_warp = true; warp_events.clear(); - mouse_mode = wanted_mouse_mode; if (show_cursor) { cursor_update_shape(); } } -void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) { - ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX); - if (p_mode == mouse_mode_base) { - return; - } - mouse_mode_base = p_mode; - _mouse_update_mode(); -} - -DisplayServer::MouseMode DisplayServerMacOS::mouse_get_mode() const { - return mouse_mode; -} - -void DisplayServerMacOS::mouse_set_mode_override(MouseMode p_mode) { - ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX); - if (p_mode == mouse_mode_override) { - return; - } - mouse_mode_override = p_mode; - _mouse_update_mode(); -} - -DisplayServer::MouseMode DisplayServerMacOS::mouse_get_mode_override() const { - return mouse_mode_override; -} - -void DisplayServerMacOS::mouse_set_mode_override_enabled(bool p_override_enabled) { - if (p_override_enabled == mouse_mode_override_enabled) { - return; - } - mouse_mode_override_enabled = p_override_enabled; - _mouse_update_mode(); -} - -bool DisplayServerMacOS::mouse_is_mode_override_enabled() const { - return mouse_mode_override_enabled; -} - bool DisplayServerMacOS::update_mouse_wrap(WindowData &p_wd, NSPoint &r_delta, NSPoint &r_mpos, NSTimeInterval p_timestamp) { _THREAD_SAFE_METHOD_ @@ -1460,10 +1403,6 @@ int DisplayServerMacOS::get_screen_count() const { return [screenArray count]; } -int DisplayServerMacOS::get_primary_screen() const { - return 0; -} - int DisplayServerMacOS::get_keyboard_focus_screen() const { const NSUInteger index = [[NSScreen screens] indexOfObject:[NSScreen mainScreen]]; return (index == NSNotFound) ? get_primary_screen() : index; @@ -1509,11 +1448,12 @@ int DisplayServerMacOS::screen_get_dpi(int p_screen) const { NSArray *screenArray = [NSScreen screens]; if ((NSUInteger)p_screen < [screenArray count]) { - NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription]; + NSScreen *screen = [screenArray objectAtIndex:p_screen]; + NSDictionary *description = [screen deviceDescription]; const NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue]; - const CGSize displayPhysicalSize = CGDisplayScreenSize([[description objectForKey:@"NSScreenNumber"] unsignedIntValue]); - float scale = [[screenArray objectAtIndex:p_screen] backingScaleFactor]; + const CGSize displayPhysicalSize = CGDisplayScreenSize(_get_display_id_for_screen(screen)); + float scale = [screen backingScaleFactor]; float den2 = (displayPhysicalSize.width / 25.4f) * (displayPhysicalSize.width / 25.4f) + (displayPhysicalSize.height / 25.4f) * (displayPhysicalSize.height / 25.4f); if (den2 > 0.0f) { @@ -1711,42 +1651,6 @@ Ref DisplayServerMacOS::screen_get_image_rect(const Rect2i &p_rect) const return img; } -float DisplayServerMacOS::screen_get_refresh_rate(int p_screen) const { - _THREAD_SAFE_METHOD_ - - p_screen = _get_screen_index(p_screen); - int screen_count = get_screen_count(); - ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_REFRESH_RATE_FALLBACK); - - NSArray *screenArray = [NSScreen screens]; - if ((NSUInteger)p_screen < [screenArray count]) { - NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription]; - const CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode([[description objectForKey:@"NSScreenNumber"] unsignedIntValue]); - const double displayRefreshRate = CGDisplayModeGetRefreshRate(displayMode); - return (float)displayRefreshRate; - } - ERR_PRINT("An error occurred while trying to get the screen refresh rate."); - return SCREEN_REFRESH_RATE_FALLBACK; -} - -bool DisplayServerMacOS::screen_is_kept_on() const { - return (screen_keep_on_assertion); -} - -void DisplayServerMacOS::screen_set_keep_on(bool p_enable) { - if (screen_keep_on_assertion) { - IOPMAssertionRelease(screen_keep_on_assertion); - screen_keep_on_assertion = kIOPMNullAssertionID; - } - - if (p_enable) { - String app_name_string = GLOBAL_GET("application/config/name"); - NSString *name = [NSString stringWithUTF8String:(app_name_string.is_empty() ? "Godot Engine" : app_name_string.utf8().get_data())]; - NSString *reason = @"Godot Engine running with display/window/energy_saving/keep_screen_on = true"; - IOPMAssertionCreateWithDescription(kIOPMAssertPreventUserIdleDisplaySleep, (__bridge CFStringRef)name, (__bridge CFStringRef)reason, (__bridge CFStringRef)reason, nullptr, 0, nullptr, &screen_keep_on_assertion); - } -} - Vector DisplayServerMacOS::get_window_list() const { _THREAD_SAFE_METHOD_ @@ -1761,11 +1665,16 @@ DisplayServer::WindowID DisplayServerMacOS::create_sub_window(WindowMode p_mode, _THREAD_SAFE_METHOD_ WindowID id = _create_window(p_mode, p_vsync_mode, p_rect); - for (int i = 0; i < WINDOW_FLAG_MAX; i++) { - if (p_flags & (1 << i)) { - window_set_flag(WindowFlags(i), true, id); - } + + uint32_t set_flags = p_flags & ~(WINDOW_FLAG_MAX - 1); // Clear the flags that are not supported by the window. + while (set_flags != 0) { + // Find the index of the next set bit. + uint32_t index = (uint32_t)__builtin_ctzll(set_flags); + // Clear the set bit. + set_flags &= (set_flags - 1); + window_set_flag(WindowFlags(index), true, id); } + #ifdef RD_ENABLED if (rendering_device) { rendering_device->screen_create(id); @@ -1975,6 +1884,8 @@ void DisplayServerMacOS::reparent_check(WindowID p_window) { WindowData &wd = windows[p_window]; NSScreen *screen = [wd.window_object screen]; + _update_hdr_output(p_window, wd.hdr_output); + _window_update_display_id(&wd); if (wd.transient_parent != INVALID_WINDOW_ID) { @@ -2925,28 +2836,49 @@ DisplayServer::VSyncMode DisplayServerMacOS::window_get_vsync_mode(WindowID p_wi return DisplayServer::VSYNC_ENABLED; } -int DisplayServerMacOS::accessibility_should_increase_contrast() const { - return [(GodotApplicationDelegate *)[[NSApplication sharedApplication] delegate] getHighContrast]; +DisplayServerMacOSBase::HDROutput &DisplayServerMacOS::_get_hdr_output(WindowID p_window) { + return windows.getptr(p_window)->hdr_output; } -int DisplayServerMacOS::accessibility_should_reduce_animation() const { - return [(GodotApplicationDelegate *)[[NSApplication sharedApplication] delegate] getReduceMotion]; +const DisplayServerMacOSBase::HDROutput &DisplayServerMacOS::_get_hdr_output(WindowID p_window) const { + return windows.getptr(p_window)->hdr_output; } -int DisplayServerMacOS::accessibility_should_reduce_transparency() const { - return [(GodotApplicationDelegate *)[[NSApplication sharedApplication] delegate] getReduceTransparency]; +void DisplayServerMacOS::window_get_edr_values(WindowID p_window, CGFloat *r_max_potential_edr_value, CGFloat *r_max_edr_value) const { + _THREAD_SAFE_METHOD_ + + const WindowData *wd = windows.getptr(p_window); + ERR_FAIL_NULL(wd); + + NSScreen *screen = wd->window_object.screen; +#define SET_VAL(v, val) \ + if (v) { \ + *v = val; \ + } + + if (@available(macOS 10.15, *)) { + SET_VAL(r_max_potential_edr_value, screen.maximumPotentialExtendedDynamicRangeColorComponentValue); + SET_VAL(r_max_edr_value, screen.maximumExtendedDynamicRangeColorComponentValue); + } else { + SET_VAL(r_max_potential_edr_value, 1.0); + SET_VAL(r_max_edr_value, 1.0); + } + +#undef SET_VAL } -int DisplayServerMacOS::accessibility_screen_reader_active() const { - return [(GodotApplicationDelegate *)[[NSApplication sharedApplication] delegate] getVoiceOver]; -} +void DisplayServerMacOS::update_screen_parameters() { + for (const KeyValue &E : windows) { + if (E.value.hdr_output.requested) { + _update_hdr_output(E.key, E.value.hdr_output); + } + } -Point2i DisplayServerMacOS::ime_get_selection() const { - return im_selection; -} - -String DisplayServerMacOS::ime_get_text() const { - return im_text; +#ifdef TOOLS_ENABLED + for (KeyValue &E : embedded_processes) { + E.value.process->display_state_changed(); + } +#endif } void DisplayServerMacOS::cursor_update_shape() { @@ -3031,10 +2963,6 @@ void DisplayServerMacOS::cursor_set_shape(CursorShape p_shape) { cursor_update_shape(); } -DisplayServerMacOS::CursorShape DisplayServerMacOS::cursor_get_shape() const { - return cursor_shape; -} - void DisplayServerMacOS::cursor_set_custom_image(const Ref &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { _THREAD_SAFE_METHOD_ @@ -3135,7 +3063,7 @@ uint32_t DisplayServerMacOS::window_get_display_id(WindowID p_window) const { void DisplayServerMacOS::_window_update_display_id(WindowData *p_wd) { NSScreen *screen = [p_wd->window_object screen]; - CGDirectDisplayID display_id = [[screen deviceDescription][@"NSScreenNumber"] unsignedIntValue]; + CGDirectDisplayID display_id = _get_display_id_for_screen(screen); if (p_wd->display_id == display_id) { return; } @@ -3925,6 +3853,15 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM } force_process_and_drop_events(); + __block DisplayServerMacOS *self = this; + screen_observer = [NSNotificationCenter.defaultCenter + addObserverForName:NSApplicationDidChangeScreenParametersNotification + object:nil + queue:nil + usingBlock:^(NSNotification *_Nonnull note) { + self->update_screen_parameters(); + }]; + if (rendering_driver == "dummy") { RasterizerDummy::make_current(); } @@ -3946,16 +3883,9 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM RendererCompositorRD::make_current(); } #endif - - screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on")); } DisplayServerMacOS::~DisplayServerMacOS() { - if (screen_keep_on_assertion) { - IOPMAssertionRelease(screen_keep_on_assertion); - screen_keep_on_assertion = kIOPMNullAssertionID; - } - // Destroy all status indicators. for (HashMap::Iterator E = indicators.begin(); E; ++E) { [[NSStatusBar systemStatusBar] removeStatusItem:E->value.item]; @@ -4002,6 +3932,8 @@ DisplayServerMacOS::~DisplayServerMacOS() { memdelete(accessibility_driver); } #endif + [NSNotificationCenter.defaultCenter removeObserver:screen_observer]; + CGDisplayRemoveReconfigurationCallback(_displays_arrangement_changed, nullptr); cursors_cache.clear(); diff --git a/platform/macos/display_server_macos_base.h b/platform/macos/display_server_macos_base.h index 2fbc08a45c..eb7a0e3e50 100644 --- a/platform/macos/display_server_macos_base.h +++ b/platform/macos/display_server_macos_base.h @@ -35,13 +35,18 @@ #define FontVariation __FontVariation #import +#import #undef FontVariation +class RenderingContextDriver; +class RenderingDevice; + class DisplayServerMacOSBase : public DisplayServer { GDSOFTCLASS(DisplayServerMacOSBase, DisplayServer) id tts = nullptr; + IOPMAssertionID screen_keep_on_assertion = kIOPMNullAssertionID; struct LayoutInfo { String name; @@ -53,21 +58,100 @@ class DisplayServerMacOSBase : public DisplayServer { Callable system_theme_changed; + constexpr static CGFloat HARDWARE_REFERENCE_LUMINANCE_NITS = 100.0f; + +public: + struct HDROutput { + static constexpr float AUTO_MAX_LUMINANCE = -1.0f; + + bool requested = false; + float max_luminance = AUTO_MAX_LUMINANCE; + + bool is_auto_max_luminance() const { return max_luminance < 0.0f; } + }; + protected: _THREAD_SAFE_CLASS_ + MouseMode mouse_mode = MOUSE_MODE_VISIBLE; + MouseMode mouse_mode_base = MOUSE_MODE_VISIBLE; + MouseMode mouse_mode_override = MOUSE_MODE_VISIBLE; + bool mouse_mode_override_enabled = false; + + void _mouse_update_mode(); + virtual void _mouse_apply_mode(MouseMode p_prev_mode, MouseMode p_new_mode) = 0; + + String im_text; + Point2i im_selection; + + CursorShape cursor_shape = CURSOR_ARROW; + void initialize_tts() const; + static CGDirectDisplayID _get_display_id_for_screen(NSScreen *p_screen); + void _update_keyboard_layouts() const; static void _keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info); +#if defined(RD_ENABLED) + RenderingContextDriver *rendering_context = nullptr; + RenderingDevice *rendering_device = nullptr; +#endif + + virtual HDROutput &_get_hdr_output(WindowID p_window) = 0; + virtual const HDROutput &_get_hdr_output(WindowID p_window) const = 0; + + constexpr float _calculate_current_reference_luminance(CGFloat p_max_potential_edr_value, CGFloat p_max_edr_value) const; + void _update_hdr_output(WindowID p_window, const HDROutput &p_hdr); + public: + virtual bool tts_is_speaking() const override; + virtual bool tts_is_paused() const override; + virtual TypedArray tts_get_voices() const override; + + virtual void tts_speak(const String &p_text, const String &p_voice, int p_volume = 50, float p_pitch = 1.f, float p_rate = 1.f, int64_t p_utterance_id = 0, bool p_interrupt = false) override; + virtual void tts_pause() override; + virtual void tts_resume() override; + virtual void tts_stop() override; + + void emit_system_theme_changed(); + virtual bool is_dark_mode_supported() const override; + virtual bool is_dark_mode() const override; + virtual Color get_accent_color() const override; + virtual Color get_base_color() const override; + virtual void set_system_theme_change_callback(const Callable &p_callable) override; + + virtual void mouse_set_mode(MouseMode p_mode) override; + virtual MouseMode mouse_get_mode() const override; + virtual void mouse_set_mode_override(MouseMode p_mode) override; + virtual MouseMode mouse_get_mode_override() const override; + virtual void mouse_set_mode_override_enabled(bool p_override_enabled) override; + virtual bool mouse_is_mode_override_enabled() const override; + virtual void clipboard_set(const String &p_text) override; virtual String clipboard_get() const override; virtual Ref clipboard_get_image() const override; virtual bool clipboard_has() const override; virtual bool clipboard_has_image() const override; + virtual int get_primary_screen() const override; + virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; + virtual void screen_set_keep_on(bool p_enable) override; + virtual bool screen_is_kept_on() const override; + + virtual int accessibility_should_increase_contrast() const override; + virtual int accessibility_should_reduce_animation() const override; + virtual int accessibility_should_reduce_transparency() const override; + virtual int accessibility_screen_reader_active() const override; + + void update_im_text(const Point2i &p_selection, const String &p_text); + virtual Point2i ime_get_selection() const override; + virtual String ime_get_text() const override; + + virtual CursorShape cursor_get_shape() const override; + + virtual void beep() const override; + virtual int keyboard_get_layout_count() const override; virtual int keyboard_get_current_layout() const override; virtual void keyboard_set_current_layout(int p_index) override; @@ -77,21 +161,21 @@ public: virtual Key keyboard_get_label_from_physical(Key p_keycode) const override; virtual void show_emoji_and_symbol_picker() const override; - void emit_system_theme_changed(); - virtual bool is_dark_mode_supported() const override; - virtual bool is_dark_mode() const override; - virtual Color get_accent_color() const override; - virtual Color get_base_color() const override; - virtual void set_system_theme_change_callback(const Callable &p_callable) override; + virtual void window_get_edr_values(WindowID p_window, CGFloat *r_max_potential_edr_value = nullptr, CGFloat *r_max_edr_value = nullptr) const = 0; + virtual bool window_is_hdr_output_supported(WindowID p_window = MAIN_WINDOW_ID) const override; + virtual void window_request_hdr_output(const bool p_enable, WindowID p_window = MAIN_WINDOW_ID) override; + virtual bool window_is_hdr_output_requested(WindowID p_window = MAIN_WINDOW_ID) const override; + virtual bool window_is_hdr_output_enabled(WindowID p_window = MAIN_WINDOW_ID) const override; - virtual bool tts_is_speaking() const override; - virtual bool tts_is_paused() const override; - virtual TypedArray tts_get_voices() const override; + virtual void window_set_hdr_output_reference_luminance(const float p_reference_luminance, WindowID p_window = MAIN_WINDOW_ID) override; + virtual float window_get_hdr_output_reference_luminance(WindowID p_window = MAIN_WINDOW_ID) const override; + virtual float window_get_hdr_output_current_reference_luminance(WindowID p_window = MAIN_WINDOW_ID) const override; - virtual void tts_speak(const String &p_text, const String &p_voice, int p_volume = 50, float p_pitch = 1.f, float p_rate = 1.f, int64_t p_utterance_id = 0, bool p_interrupt = false) override; - virtual void tts_pause() override; - virtual void tts_resume() override; - virtual void tts_stop() override; + virtual void window_set_hdr_output_max_luminance(const float p_max_luminance, WindowID p_window = MAIN_WINDOW_ID) override; + virtual float window_get_hdr_output_max_luminance(WindowID p_window = MAIN_WINDOW_ID) const override; + virtual float window_get_hdr_output_current_max_luminance(WindowID p_window = MAIN_WINDOW_ID) const override; + + virtual float window_get_output_max_linear_value(WindowID p_window = MAIN_WINDOW_ID) const override; DisplayServerMacOSBase(); ~DisplayServerMacOSBase(); diff --git a/platform/macos/display_server_macos_base.mm b/platform/macos/display_server_macos_base.mm index f0e62b577b..076ee8f670 100644 --- a/platform/macos/display_server_macos_base.mm +++ b/platform/macos/display_server_macos_base.mm @@ -29,14 +29,73 @@ /**************************************************************************/ #import "display_server_macos_base.h" +#import "godot_application_delegate.h" #import "key_mapping_macos.h" #import "tts_macos.h" #include "core/config/project_settings.h" +#include "core/os/main_loop.h" #include "drivers/png/png_driver_common.h" +#if defined(RD_ENABLED) +#include "servers/rendering/rendering_context_driver.h" +#include "servers/rendering/rendering_device.h" +#endif + #import +void DisplayServerMacOSBase::_mouse_update_mode() { + MouseMode wanted_mouse_mode = mouse_mode_override_enabled + ? mouse_mode_override + : mouse_mode_base; + + if (wanted_mouse_mode == mouse_mode) { + return; + } + + MouseMode prev_mode = mouse_mode; + mouse_mode = wanted_mouse_mode; + _mouse_apply_mode(prev_mode, wanted_mouse_mode); +} + +void DisplayServerMacOSBase::mouse_set_mode(MouseMode p_mode) { + ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX); + if (p_mode == mouse_mode_base) { + return; + } + mouse_mode_base = p_mode; + _mouse_update_mode(); +} + +DisplayServer::MouseMode DisplayServerMacOSBase::mouse_get_mode() const { + return mouse_mode; +} + +void DisplayServerMacOSBase::mouse_set_mode_override(MouseMode p_mode) { + ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX); + if (p_mode == mouse_mode_override) { + return; + } + mouse_mode_override = p_mode; + _mouse_update_mode(); +} + +DisplayServer::MouseMode DisplayServerMacOSBase::mouse_get_mode_override() const { + return mouse_mode_override; +} + +void DisplayServerMacOSBase::mouse_set_mode_override_enabled(bool p_override_enabled) { + if (p_override_enabled == mouse_mode_override_enabled) { + return; + } + mouse_mode_override_enabled = p_override_enabled; + _mouse_update_mode(); +} + +bool DisplayServerMacOSBase::mouse_is_mode_override_enabled() const { + return mouse_mode_override_enabled; +} + void DisplayServerMacOSBase::clipboard_set(const String &p_text) { _THREAD_SAFE_METHOD_ @@ -103,6 +162,14 @@ bool DisplayServerMacOSBase::clipboard_has_image() const { return result; } +CGDirectDisplayID DisplayServerMacOSBase::_get_display_id_for_screen(NSScreen *p_screen) { + if (@available(macOS 26.0, *)) { + return [p_screen CGDirectDisplayID]; + } else { + return [[p_screen deviceDescription][@"NSScreenNumber"] unsignedIntValue]; + } +} + void DisplayServerMacOSBase::initialize_tts() const { const_cast(this)->tts = [[TTS_MacOS alloc] init]; } @@ -384,6 +451,85 @@ void DisplayServerMacOSBase::set_system_theme_change_callback(const Callable &p_ system_theme_changed = p_callable; } +bool DisplayServerMacOSBase::screen_is_kept_on() const { + return (screen_keep_on_assertion); +} + +void DisplayServerMacOSBase::screen_set_keep_on(bool p_enable) { + if (screen_keep_on_assertion) { + IOPMAssertionRelease(screen_keep_on_assertion); + screen_keep_on_assertion = kIOPMNullAssertionID; + } + + if (p_enable) { + String app_name_string = GLOBAL_GET("application/config/name"); + NSString *name = [NSString stringWithUTF8String:(app_name_string.is_empty() ? "Godot Engine" : app_name_string.utf8().get_data())]; + NSString *reason = @"Godot Engine running with display/window/energy_saving/keep_screen_on = true"; + IOPMAssertionCreateWithDescription(kIOPMAssertPreventUserIdleDisplaySleep, (__bridge CFStringRef)name, (__bridge CFStringRef)reason, (__bridge CFStringRef)reason, nullptr, 0, nullptr, &screen_keep_on_assertion); + } +} + +int DisplayServerMacOSBase::accessibility_should_increase_contrast() const { + return [(GodotApplicationDelegate *)[[NSApplication sharedApplication] delegate] getHighContrast]; +} + +int DisplayServerMacOSBase::accessibility_should_reduce_animation() const { + return [(GodotApplicationDelegate *)[[NSApplication sharedApplication] delegate] getReduceMotion]; +} + +int DisplayServerMacOSBase::accessibility_should_reduce_transparency() const { + return [(GodotApplicationDelegate *)[[NSApplication sharedApplication] delegate] getReduceTransparency]; +} + +int DisplayServerMacOSBase::accessibility_screen_reader_active() const { + return [(GodotApplicationDelegate *)[[NSApplication sharedApplication] delegate] getVoiceOver]; +} + +void DisplayServerMacOSBase::update_im_text(const Point2i &p_selection, const String &p_text) { + im_selection = p_selection; + im_text = p_text; + + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE); +} + +Point2i DisplayServerMacOSBase::ime_get_selection() const { + return im_selection; +} + +String DisplayServerMacOSBase::ime_get_text() const { + return im_text; +} + +DisplayServer::CursorShape DisplayServerMacOSBase::cursor_get_shape() const { + return cursor_shape; +} + +void DisplayServerMacOSBase::beep() const { + NSBeep(); +} + +int DisplayServerMacOSBase::get_primary_screen() const { + return 0; +} + +float DisplayServerMacOSBase::screen_get_refresh_rate(int p_screen) const { + _THREAD_SAFE_METHOD_ + + p_screen = _get_screen_index(p_screen); + int screen_count = get_screen_count(); + ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_REFRESH_RATE_FALLBACK); + + NSArray *screenArray = [NSScreen screens]; + if ((NSUInteger)p_screen < [screenArray count]) { + NSScreen *screen = [screenArray objectAtIndex:p_screen]; + const CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode(_get_display_id_for_screen(screen)); + const double displayRefreshRate = CGDisplayModeGetRefreshRate(displayMode); + return (float)displayRefreshRate; + } + ERR_PRINT("An error occurred while trying to get the screen refresh rate."); + return SCREEN_REFRESH_RATE_FALLBACK; +} + void DisplayServerMacOSBase::emit_system_theme_changed() { if (system_theme_changed.is_valid()) { Variant ret; @@ -395,6 +541,138 @@ void DisplayServerMacOSBase::emit_system_theme_changed() { } } +// MARK: - HDR / EDR + +void DisplayServerMacOSBase::_update_hdr_output(WindowID p_window, const HDROutput &p_hdr) { +#ifdef RD_ENABLED + if (!rendering_context) { + return; + } + + CGFloat max_potential_edr, max_edr; + window_get_edr_values(p_window, &max_potential_edr, &max_edr); + bool desired_hdr_enabled = p_hdr.requested && max_potential_edr > 1.0f; + bool current_hdr_enabled = rendering_context->window_get_hdr_output_enabled(p_window); + if (current_hdr_enabled != desired_hdr_enabled) { + rendering_context->window_set_hdr_output_enabled(p_window, desired_hdr_enabled); + } + + float reference_luminance = _calculate_current_reference_luminance(max_potential_edr, max_edr); + rendering_context->window_set_hdr_output_reference_luminance(p_window, reference_luminance); + + float max_luminance = p_hdr.is_auto_max_luminance() ? max_potential_edr * HARDWARE_REFERENCE_LUMINANCE_NITS : p_hdr.max_luminance; + rendering_context->window_set_hdr_output_max_luminance(p_window, max_luminance); +#endif +} + +bool DisplayServerMacOSBase::window_is_hdr_output_supported(WindowID p_window) const { + _THREAD_SAFE_METHOD_ + +#if defined(RD_ENABLED) + if (rendering_device && !rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT)) { + return false; + } +#endif + CGFloat max_potential_edr; + window_get_edr_values(p_window, &max_potential_edr, nullptr); + return max_potential_edr > 1.0f; +} + +void DisplayServerMacOSBase::window_request_hdr_output(const bool p_enabled, WindowID p_window) { + _THREAD_SAFE_METHOD_ + +#if defined(RD_ENABLED) + ERR_FAIL_COND_MSG(p_enabled && rendering_device && !rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT), "HDR output is not supported by the rendering device."); +#endif + + HDROutput &hdr = _get_hdr_output(p_window); + hdr.requested = p_enabled; + _update_hdr_output(p_window, hdr); +} + +bool DisplayServerMacOSBase::window_is_hdr_output_requested(WindowID p_window) const { + _THREAD_SAFE_METHOD_ + + return _get_hdr_output(p_window).requested; +} + +bool DisplayServerMacOSBase::window_is_hdr_output_enabled(WindowID p_window) const { + _THREAD_SAFE_METHOD_ + +#if defined(RD_ENABLED) + if (rendering_context) { + return rendering_context->window_get_hdr_output_enabled(p_window); + } +#endif + + return false; +} + +void DisplayServerMacOSBase::window_set_hdr_output_reference_luminance(const float p_reference_luminance, WindowID p_window) { + ERR_PRINT_ONCE("Manually setting reference white luminance is not supported on Apple devices, as they provide a user-facing brightness setting that directly controls reference white luminance."); +} + +float DisplayServerMacOSBase::window_get_hdr_output_reference_luminance(WindowID p_window) const { + return -1.0f; // Always auto-adjusted by the OS on Apple platforms. +} + +constexpr float DisplayServerMacOSBase::_calculate_current_reference_luminance(CGFloat p_max_potential_edr_value, CGFloat p_max_edr_value) const { + return (p_max_potential_edr_value * HARDWARE_REFERENCE_LUMINANCE_NITS) / p_max_edr_value; +} + +float DisplayServerMacOSBase::window_get_hdr_output_current_reference_luminance(WindowID p_window) const { + _THREAD_SAFE_METHOD_ + +#if defined(RD_ENABLED) + if (rendering_context) { + return rendering_context->window_get_hdr_output_reference_luminance(p_window); + } +#endif + return 200.0f; +} + +void DisplayServerMacOSBase::window_set_hdr_output_max_luminance(const float p_max_luminance, WindowID p_window) { + _THREAD_SAFE_METHOD_ + + HDROutput &hdr = _get_hdr_output(p_window); + + if (hdr.max_luminance == p_max_luminance) { + return; + } + hdr.max_luminance = p_max_luminance; + _update_hdr_output(p_window, hdr); +} + +float DisplayServerMacOSBase::window_get_hdr_output_max_luminance(WindowID p_window) const { + _THREAD_SAFE_METHOD_ + + return _get_hdr_output(p_window).max_luminance; +} + +float DisplayServerMacOSBase::window_get_hdr_output_current_max_luminance(WindowID p_window) const { + _THREAD_SAFE_METHOD_ + + const HDROutput &hdr = _get_hdr_output(p_window); + if (hdr.is_auto_max_luminance()) { + CGFloat max_potential_edr; + window_get_edr_values(p_window, &max_potential_edr, nullptr); + return max_potential_edr * HARDWARE_REFERENCE_LUMINANCE_NITS; + } + return hdr.max_luminance; +} + +float DisplayServerMacOSBase::window_get_output_max_linear_value(WindowID p_window) const { + _THREAD_SAFE_METHOD_ + +#if defined(RD_ENABLED) + if (rendering_context) { + return rendering_context->window_get_output_max_linear_value(p_window); + } +#endif + + return 1.0f; // SDR +} + DisplayServerMacOSBase::DisplayServerMacOSBase() { KeyMappingMacOS::initialize(); @@ -404,6 +682,8 @@ DisplayServerMacOSBase::DisplayServerMacOSBase() { initialize_tts(); } + screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on")); + // Register to be notified on keyboard layout changes. CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), nullptr, _keyboard_layout_changed, @@ -412,5 +692,10 @@ DisplayServerMacOSBase::DisplayServerMacOSBase() { } DisplayServerMacOSBase::~DisplayServerMacOSBase() { + if (screen_keep_on_assertion) { + IOPMAssertionRelease(screen_keep_on_assertion); + screen_keep_on_assertion = kIOPMNullAssertionID; + } + CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), nullptr, kTISNotifySelectedKeyboardInputSourceChanged, nullptr); } diff --git a/platform/macos/display_server_embedded.h b/platform/macos/display_server_macos_embedded.h similarity index 83% rename from platform/macos/display_server_embedded.h rename to platform/macos/display_server_macos_embedded.h index afd71e0711..7072e8991b 100644 --- a/platform/macos/display_server_embedded.h +++ b/platform/macos/display_server_macos_embedded.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* display_server_embedded.h */ +/* display_server_macos_embedded.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -40,7 +40,7 @@ class NativeMenu; class RenderingContextDriver; class RenderingDevice; -struct DisplayServerEmbeddedState { +struct DisplayServerMacOSEmbeddedState { /*! Default to a scale of 2.0, which is the most common. */ float screen_max_scale = 2.0f; float screen_dpi = 96.0f; @@ -49,18 +49,24 @@ struct DisplayServerEmbeddedState { /*! The display ID of the window which is displaying the embedded process content. */ uint32_t display_id = -1; + /*! The current maximum EDR value for the display. */ + double screen_max_edr = 1.0; + /*! The maximum possible EDR value for the display. */ + double screen_max_potential_edr = 1.0; + void serialize(PackedByteArray &r_data); Error deserialize(const PackedByteArray &p_data); - _FORCE_INLINE_ bool operator==(const DisplayServerEmbeddedState &p_other) const { - return screen_max_scale == p_other.screen_max_scale && screen_dpi == p_other.screen_dpi && display_id == p_other.display_id; + _FORCE_INLINE_ bool operator==(const DisplayServerMacOSEmbeddedState &p_other) const { + return screen_max_scale == p_other.screen_max_scale && screen_dpi == p_other.screen_dpi && screen_window_scale == p_other.screen_window_scale && display_id == p_other.display_id && screen_max_edr == p_other.screen_max_edr && screen_max_potential_edr == p_other.screen_max_potential_edr; } }; -class DisplayServerEmbedded : public DisplayServerMacOSBase { - GDSOFTCLASS(DisplayServerEmbedded, DisplayServerMacOSBase) +/// "Embedded" as in "Embedded in the Godot editor window". +class DisplayServerMacOSEmbedded : public DisplayServerMacOSBase { + GDSOFTCLASS(DisplayServerMacOSEmbedded, DisplayServerMacOSBase) - DisplayServerEmbeddedState state; + DisplayServerMacOSEmbeddedState state; NativeMenu *native_menu = nullptr; @@ -82,24 +88,16 @@ class DisplayServerEmbedded : public DisplayServerMacOSBase { GLManagerEmbedded *gl_manager = nullptr; #endif -#if defined(RD_ENABLED) - RenderingContextDriver *rendering_context = nullptr; - RenderingDevice *rendering_device = nullptr; -#endif - String rendering_driver; + HDROutput hdr_output; + + HDROutput &_get_hdr_output(WindowID p_window) override; + const HDROutput &_get_hdr_output(WindowID p_window) const override; + Point2i ime_last_position; - Point2i im_selection; - String im_text; - MouseMode mouse_mode = MOUSE_MODE_VISIBLE; - MouseMode mouse_mode_base = MOUSE_MODE_VISIBLE; - MouseMode mouse_mode_override = MOUSE_MODE_VISIBLE; - bool mouse_mode_override_enabled = false; - void _mouse_update_mode(); - - CursorShape cursor_shape = CURSOR_ARROW; + void _mouse_apply_mode(MouseMode p_prev_mode, MouseMode p_new_mode) override; struct Joy { String name; @@ -134,16 +132,7 @@ public: void send_window_event(DisplayServer::WindowEvent p_event, DisplayServer::WindowID p_id = MAIN_WINDOW_ID) const; void _window_callback(const Callable &p_callable, const Variant &p_arg) const; - virtual void beep() const override; - // MARK: - Mouse - virtual void mouse_set_mode(MouseMode p_mode) override; - virtual MouseMode mouse_get_mode() const override; - virtual void mouse_set_mode_override(MouseMode p_mode) override; - virtual MouseMode mouse_get_mode_override() const override; - virtual void mouse_set_mode_override_enabled(bool p_override_enabled) override; - virtual bool mouse_is_mode_override_enabled() const override; - virtual void warp_mouse(const Point2i &p_position) override; virtual Point2i mouse_get_position() const override; virtual BitField mouse_get_button_state() const override; @@ -154,14 +143,11 @@ public: virtual String get_name() const override; virtual int get_screen_count() const override; - virtual int get_primary_screen() const override; virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; - virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; - virtual Vector get_window_list() const override; virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override; @@ -216,17 +202,15 @@ public: virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override; virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override; - void update_im_text(const Point2i &p_selection, const String &p_text); - virtual Point2i ime_get_selection() const override; - virtual String ime_get_text() const override; - virtual void cursor_set_shape(CursorShape p_shape) override; - virtual CursorShape cursor_get_shape() const override; virtual void cursor_set_custom_image(const Ref &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override; - void set_state(const DisplayServerEmbeddedState &p_state); + void window_get_edr_values(WindowID p_window, CGFloat *r_max_potential_edr_value, CGFloat *r_max_edr_value) const override; + void update_screen_parameters(); + + void set_state(const DisplayServerMacOSEmbeddedState &p_state); virtual void swap_buffers() override; - DisplayServerEmbedded(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::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); - ~DisplayServerEmbedded(); + DisplayServerMacOSEmbedded(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::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); + ~DisplayServerMacOSEmbedded(); }; diff --git a/platform/macos/display_server_embedded.mm b/platform/macos/display_server_macos_embedded.mm similarity index 66% rename from platform/macos/display_server_embedded.mm rename to platform/macos/display_server_macos_embedded.mm index 522fe7384f..4f9062ad61 100644 --- a/platform/macos/display_server_embedded.mm +++ b/platform/macos/display_server_macos_embedded.mm @@ -1,5 +1,5 @@ /**************************************************************************/ -/* display_server_embedded.mm */ +/* display_server_macos_embedded.mm */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#import "display_server_embedded.h" +#import "display_server_macos_embedded.h" #if defined(GLES3_ENABLED) #import "embedded_gl_manager.h" @@ -60,7 +60,7 @@ #import "core/os/main_loop.h" #import "servers/display/native_menu.h" -DisplayServerEmbedded::DisplayServerEmbedded(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::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) { +DisplayServerMacOSEmbedded::DisplayServerMacOSEmbedded(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::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) { EmbeddedDebugger::initialize(this); r_error = OK; // default to OK @@ -215,7 +215,7 @@ DisplayServerEmbedded::DisplayServerEmbedded(const String &p_rendering_driver, W } } -DisplayServerEmbedded::~DisplayServerEmbedded() { +DisplayServerMacOSEmbedded::~DisplayServerMacOSEmbedded() { if (native_menu) { memdelete(native_menu); native_menu = nullptr; @@ -243,11 +243,11 @@ DisplayServerEmbedded::~DisplayServerEmbedded() { #endif } -DisplayServer *DisplayServerEmbedded::create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t /* p_parent_window */, Error &r_error) { - return memnew(DisplayServerEmbedded(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error)); +DisplayServer *DisplayServerMacOSEmbedded::create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t /* p_parent_window */, Error &r_error) { + return memnew(DisplayServerMacOSEmbedded(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error)); } -Vector DisplayServerEmbedded::get_rendering_drivers_func() { +Vector DisplayServerMacOSEmbedded::get_rendering_drivers_func() { Vector drivers; #if defined(VULKAN_ENABLED) @@ -263,74 +263,23 @@ Vector DisplayServerEmbedded::get_rendering_drivers_func() { return drivers; } -void DisplayServerEmbedded::register_embedded_driver() { +void DisplayServerMacOSEmbedded::register_embedded_driver() { register_create_function("embedded", create_func, get_rendering_drivers_func); } -void DisplayServerEmbedded::beep() const { - NSBeep(); -} - // MARK: - Mouse -void DisplayServerEmbedded::_mouse_update_mode() { - MouseMode wanted_mouse_mode = mouse_mode_override_enabled - ? mouse_mode_override - : mouse_mode_base; - - if (wanted_mouse_mode == mouse_mode) { - return; - } - - EngineDebugger::get_singleton()->send_message("game_view:mouse_set_mode", { wanted_mouse_mode }); - - mouse_mode = wanted_mouse_mode; +void DisplayServerMacOSEmbedded::_mouse_apply_mode(MouseMode p_prev_mode, MouseMode p_new_mode) { + EngineDebugger::get_singleton()->send_message("game_view:mouse_set_mode", { p_new_mode }); } -void DisplayServerEmbedded::mouse_set_mode(MouseMode p_mode) { - if (p_mode == mouse_mode_base) { - return; - } - mouse_mode_base = p_mode; - _mouse_update_mode(); -} - -DisplayServerEmbedded::MouseMode DisplayServerEmbedded::mouse_get_mode() const { - return mouse_mode; -} - -void DisplayServerEmbedded::mouse_set_mode_override(MouseMode p_mode) { - ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX); - if (p_mode == mouse_mode_override) { - return; - } - mouse_mode_override = p_mode; - _mouse_update_mode(); -} - -DisplayServer::MouseMode DisplayServerEmbedded::mouse_get_mode_override() const { - return mouse_mode_override; -} - -void DisplayServerEmbedded::mouse_set_mode_override_enabled(bool p_override_enabled) { - if (p_override_enabled == mouse_mode_override_enabled) { - return; - } - mouse_mode_override_enabled = p_override_enabled; - _mouse_update_mode(); -} - -bool DisplayServerEmbedded::mouse_is_mode_override_enabled() const { - return mouse_mode_override_enabled; -} - -void DisplayServerEmbedded::warp_mouse(const Point2i &p_position) { +void DisplayServerMacOSEmbedded::warp_mouse(const Point2i &p_position) { _THREAD_SAFE_METHOD_ Input::get_singleton()->set_mouse_position(p_position); EngineDebugger::get_singleton()->send_message("game_view:warp_mouse", { p_position }); } -Point2i DisplayServerEmbedded::mouse_get_position() const { +Point2i DisplayServerMacOSEmbedded::mouse_get_position() const { _THREAD_SAFE_METHOD_ const NSPoint mouse_pos = [NSEvent mouseLocation]; @@ -350,7 +299,7 @@ Point2i DisplayServerEmbedded::mouse_get_position() const { return Vector2i(); } -BitField DisplayServerEmbedded::mouse_get_button_state() const { +BitField DisplayServerMacOSEmbedded::mouse_get_button_state() const { BitField last_button_state = MouseButtonMask::NONE; NSUInteger buttons = [NSEvent pressedMouseButtons]; @@ -374,41 +323,41 @@ BitField DisplayServerEmbedded::mouse_get_button_state() const // MARK: Events -void DisplayServerEmbedded::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) { window_resize_callbacks[p_window] = p_callable; } -void DisplayServerEmbedded::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) { window_event_callbacks[p_window] = p_callable; } -void DisplayServerEmbedded::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) { input_event_callbacks[p_window] = p_callable; } -void DisplayServerEmbedded::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) { input_text_callbacks[p_window] = p_callable; } -void DisplayServerEmbedded::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) { // Not supported } -void DisplayServerEmbedded::process_events() { +void DisplayServerMacOSEmbedded::process_events() { Input *input = Input::get_singleton(); input->flush_buffered_events(); } -void DisplayServerEmbedded::_dispatch_input_events(const Ref &p_event) { +void DisplayServerMacOSEmbedded::_dispatch_input_events(const Ref &p_event) { Ref event_from_window = p_event; WindowID window_id = INVALID_WINDOW_ID; if (event_from_window.is_valid()) { window_id = event_from_window->get_window_id(); } - DisplayServerEmbedded *ds = (DisplayServerEmbedded *)DisplayServer::get_singleton(); + DisplayServerMacOSEmbedded *ds = (DisplayServerMacOSEmbedded *)DisplayServer::get_singleton(); ds->send_input_event(p_event, window_id); } -void DisplayServerEmbedded::send_input_event(const Ref &p_event, WindowID p_id) const { +void DisplayServerMacOSEmbedded::send_input_event(const Ref &p_event, WindowID p_id) const { if (p_id != INVALID_WINDOW_ID) { const Callable *cb = input_event_callbacks.getptr(p_id); if (cb) { @@ -421,21 +370,21 @@ void DisplayServerEmbedded::send_input_event(const Ref &p_event, Win } } -void DisplayServerEmbedded::send_input_text(const String &p_text, WindowID p_id) const { +void DisplayServerMacOSEmbedded::send_input_text(const String &p_text, WindowID p_id) const { const Callable *cb = input_text_callbacks.getptr(p_id); if (cb) { _window_callback(*cb, p_text); } } -void DisplayServerEmbedded::send_window_event(DisplayServer::WindowEvent p_event, WindowID p_id) const { +void DisplayServerMacOSEmbedded::send_window_event(DisplayServer::WindowEvent p_event, WindowID p_id) const { const Callable *cb = window_event_callbacks.getptr(p_id); if (cb) { _window_callback(*cb, int(p_event)); } } -void DisplayServerEmbedded::_window_callback(const Callable &p_callable, const Variant &p_arg) const { +void DisplayServerMacOSEmbedded::_window_callback(const Callable &p_callable, const Variant &p_arg) const { if (p_callable.is_valid()) { p_callable.call(p_arg); } @@ -443,7 +392,7 @@ void DisplayServerEmbedded::_window_callback(const Callable &p_callable, const V // MARK: - -bool DisplayServerEmbedded::has_feature(Feature p_feature) const { +bool DisplayServerMacOSEmbedded::has_feature(Feature p_feature) const { switch (p_feature) { #ifndef DISABLE_DEPRECATED case FEATURE_GLOBAL_MENU: { @@ -456,6 +405,7 @@ bool DisplayServerEmbedded::has_feature(Feature p_feature) const { // case FEATURE_HIDPI: // case FEATURE_ICON: // case FEATURE_MOUSE: + case FEATURE_HDR_OUTPUT: case FEATURE_MOUSE_WARP: // case FEATURE_NATIVE_DIALOG: // case FEATURE_NATIVE_ICON: @@ -472,19 +422,15 @@ bool DisplayServerEmbedded::has_feature(Feature p_feature) const { } } -String DisplayServerEmbedded::get_name() const { +String DisplayServerMacOSEmbedded::get_name() const { return "embedded"; } -int DisplayServerEmbedded::get_screen_count() const { +int DisplayServerMacOSEmbedded::get_screen_count() const { return 1; } -int DisplayServerEmbedded::get_primary_screen() const { - return 0; -} - -Point2i DisplayServerEmbedded::screen_get_position(int p_screen) const { +Point2i DisplayServerMacOSEmbedded::screen_get_position(int p_screen) const { _THREAD_SAFE_METHOD_ p_screen = _get_screen_index(p_screen); @@ -494,7 +440,7 @@ Point2i DisplayServerEmbedded::screen_get_position(int p_screen) const { return Point2i(0, 0); } -Size2i DisplayServerEmbedded::screen_get_size(int p_screen) const { +Size2i DisplayServerMacOSEmbedded::screen_get_size(int p_screen) const { _THREAD_SAFE_METHOD_ p_screen = _get_screen_index(p_screen); @@ -504,7 +450,7 @@ Size2i DisplayServerEmbedded::screen_get_size(int p_screen) const { return window_get_size(MAIN_WINDOW_ID); } -Rect2i DisplayServerEmbedded::screen_get_usable_rect(int p_screen) const { +Rect2i DisplayServerMacOSEmbedded::screen_get_usable_rect(int p_screen) const { _THREAD_SAFE_METHOD_ p_screen = _get_screen_index(p_screen); @@ -514,7 +460,7 @@ Rect2i DisplayServerEmbedded::screen_get_usable_rect(int p_screen) const { return Rect2i(screen_get_position(p_screen), screen_get_size(p_screen)); } -int DisplayServerEmbedded::screen_get_dpi(int p_screen) const { +int DisplayServerMacOSEmbedded::screen_get_dpi(int p_screen) const { _THREAD_SAFE_METHOD_ p_screen = _get_screen_index(p_screen); @@ -524,7 +470,7 @@ int DisplayServerEmbedded::screen_get_dpi(int p_screen) const { return 96; } -float DisplayServerEmbedded::screen_get_scale(int p_screen) const { +float DisplayServerMacOSEmbedded::screen_get_scale(int p_screen) const { _THREAD_SAFE_METHOD_ switch (p_screen) { @@ -539,95 +485,76 @@ float DisplayServerEmbedded::screen_get_scale(int p_screen) const { } } -float DisplayServerEmbedded::screen_get_refresh_rate(int p_screen) const { - _THREAD_SAFE_METHOD_ - - p_screen = _get_screen_index(p_screen); - int screen_count = get_screen_count(); - ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_REFRESH_RATE_FALLBACK); - - p_screen = _get_screen_index(p_screen); - NSArray *screenArray = [NSScreen screens]; - if ((NSUInteger)p_screen < [screenArray count]) { - NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription]; - const CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode([[description objectForKey:@"NSScreenNumber"] unsignedIntValue]); - const double displayRefreshRate = CGDisplayModeGetRefreshRate(displayMode); - return (float)displayRefreshRate; - } - ERR_PRINT("An error occurred while trying to get the screen refresh rate."); - return SCREEN_REFRESH_RATE_FALLBACK; -} - -Vector DisplayServerEmbedded::get_window_list() const { +Vector DisplayServerMacOSEmbedded::get_window_list() const { Vector list; list.push_back(MAIN_WINDOW_ID); return list; } -DisplayServer::WindowID DisplayServerEmbedded::get_window_at_screen_position(const Point2i &p_position) const { +DisplayServer::WindowID DisplayServerMacOSEmbedded::get_window_at_screen_position(const Point2i &p_position) const { return MAIN_WINDOW_ID; } -void DisplayServerEmbedded::window_attach_instance_id(ObjectID p_instance, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_attach_instance_id(ObjectID p_instance, WindowID p_window) { window_attached_instance_id[p_window] = p_instance; } -ObjectID DisplayServerEmbedded::window_get_attached_instance_id(WindowID p_window) const { +ObjectID DisplayServerMacOSEmbedded::window_get_attached_instance_id(WindowID p_window) const { return window_attached_instance_id[p_window]; } -void DisplayServerEmbedded::window_set_title(const String &p_title, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_title(const String &p_title, WindowID p_window) { // Not supported } -int DisplayServerEmbedded::window_get_current_screen(WindowID p_window) const { +int DisplayServerMacOSEmbedded::window_get_current_screen(WindowID p_window) const { _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(p_window != MAIN_WINDOW_ID, INVALID_SCREEN); return 0; } -void DisplayServerEmbedded::window_set_current_screen(int p_screen, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_current_screen(int p_screen, WindowID p_window) { // Not supported } -Point2i DisplayServerEmbedded::window_get_position(WindowID p_window) const { +Point2i DisplayServerMacOSEmbedded::window_get_position(WindowID p_window) const { return Point2i(); } -Point2i DisplayServerEmbedded::window_get_position_with_decorations(WindowID p_window) const { +Point2i DisplayServerMacOSEmbedded::window_get_position_with_decorations(WindowID p_window) const { return Point2i(); } -void DisplayServerEmbedded::window_set_position(const Point2i &p_position, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_position(const Point2i &p_position, WindowID p_window) { // Probably not supported for single window iOS app } -void DisplayServerEmbedded::window_set_transient(WindowID p_window, WindowID p_parent) { +void DisplayServerMacOSEmbedded::window_set_transient(WindowID p_window, WindowID p_parent) { // Not supported } -void DisplayServerEmbedded::window_set_max_size(const Size2i p_size, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_max_size(const Size2i p_size, WindowID p_window) { // Not supported } -Size2i DisplayServerEmbedded::window_get_max_size(WindowID p_window) const { +Size2i DisplayServerMacOSEmbedded::window_get_max_size(WindowID p_window) const { return Size2i(); } -void DisplayServerEmbedded::window_set_min_size(const Size2i p_size, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_min_size(const Size2i p_size, WindowID p_window) { // Not supported } -Size2i DisplayServerEmbedded::window_get_min_size(WindowID p_window) const { +Size2i DisplayServerMacOSEmbedded::window_get_min_size(WindowID p_window) const { return Size2i(); } -void DisplayServerEmbedded::window_set_size(const Size2i p_size, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_size(const Size2i p_size, WindowID p_window) { print_line("Embedded window can't be resized."); } -void DisplayServerEmbedded::_window_set_size(const Size2i p_size, WindowID p_window) { +void DisplayServerMacOSEmbedded::_window_set_size(const Size2i p_size, WindowID p_window) { [CATransaction begin]; [CATransaction setDisableActions:YES]; @@ -656,7 +583,7 @@ void DisplayServerEmbedded::_window_set_size(const Size2i p_size, WindowID p_win } } -Size2i DisplayServerEmbedded::window_get_size(WindowID p_window) const { +Size2i DisplayServerMacOSEmbedded::window_get_size(WindowID p_window) const { #if defined(RD_ENABLED) if (rendering_context) { RenderingContextDriver::SurfaceID surface = rendering_context->surface_get_from_window(p_window); @@ -674,73 +601,73 @@ Size2i DisplayServerEmbedded::window_get_size(WindowID p_window) const { return Size2i(); } -Size2i DisplayServerEmbedded::window_get_size_with_decorations(WindowID p_window) const { +Size2i DisplayServerMacOSEmbedded::window_get_size_with_decorations(WindowID p_window) const { return window_get_size(p_window); } -void DisplayServerEmbedded::window_set_mode(WindowMode p_mode, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_mode(WindowMode p_mode, WindowID p_window) { // Not supported } -DisplayServer::WindowMode DisplayServerEmbedded::window_get_mode(WindowID p_window) const { +DisplayServer::WindowMode DisplayServerMacOSEmbedded::window_get_mode(WindowID p_window) const { return WindowMode::WINDOW_MODE_WINDOWED; } -bool DisplayServerEmbedded::window_is_maximize_allowed(WindowID p_window) const { +bool DisplayServerMacOSEmbedded::window_is_maximize_allowed(WindowID p_window) const { return false; } -void DisplayServerEmbedded::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) { if (p_flag == WINDOW_FLAG_TRANSPARENT && p_window == MAIN_WINDOW_ID) { transparent = p_enabled; layer.opaque = !(OS::get_singleton()->is_layered_allowed() && transparent); } } -bool DisplayServerEmbedded::window_get_flag(WindowFlags p_flag, WindowID p_window) const { +bool DisplayServerMacOSEmbedded::window_get_flag(WindowFlags p_flag, WindowID p_window) const { if (p_flag == WINDOW_FLAG_TRANSPARENT && p_window == MAIN_WINDOW_ID) { return transparent; } return false; } -void DisplayServerEmbedded::window_request_attention(WindowID p_window) { +void DisplayServerMacOSEmbedded::window_request_attention(WindowID p_window) { // Not supported } -void DisplayServerEmbedded::window_set_taskbar_progress_value(float p_value, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_taskbar_progress_value(float p_value, WindowID p_window) { // Not supported. } -void DisplayServerEmbedded::window_set_taskbar_progress_state(ProgressState p_state, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_taskbar_progress_state(ProgressState p_state, WindowID p_window) { // Not supported. } -void DisplayServerEmbedded::window_move_to_foreground(WindowID p_window) { +void DisplayServerMacOSEmbedded::window_move_to_foreground(WindowID p_window) { // Not supported } -bool DisplayServerEmbedded::window_is_focused(WindowID p_window) const { +bool DisplayServerMacOSEmbedded::window_is_focused(WindowID p_window) const { return true; } -float DisplayServerEmbedded::screen_get_max_scale() const { +float DisplayServerMacOSEmbedded::screen_get_max_scale() const { return state.screen_max_scale; } -bool DisplayServerEmbedded::window_can_draw(WindowID p_window) const { +bool DisplayServerMacOSEmbedded::window_can_draw(WindowID p_window) const { return true; } -bool DisplayServerEmbedded::can_any_window_draw() const { +bool DisplayServerMacOSEmbedded::can_any_window_draw() const { return true; } -void DisplayServerEmbedded::window_set_ime_active(const bool p_active, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_ime_active(const bool p_active, WindowID p_window) { EngineDebugger::get_singleton()->send_message("game_view:window_set_ime_active", { p_active }); } -void DisplayServerEmbedded::window_set_ime_position(const Point2i &p_pos, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_ime_position(const Point2i &p_pos, WindowID p_window) { if (p_pos == ime_last_position) { return; } @@ -748,7 +675,40 @@ void DisplayServerEmbedded::window_set_ime_position(const Point2i &p_pos, Window ime_last_position = p_pos; } -void DisplayServerEmbedded::set_state(const DisplayServerEmbeddedState &p_state) { +DisplayServerMacOSBase::HDROutput &DisplayServerMacOSEmbedded::_get_hdr_output(WindowID p_window) { + return hdr_output; +} + +const DisplayServerMacOSBase::HDROutput &DisplayServerMacOSEmbedded::_get_hdr_output(WindowID p_window) const { + return hdr_output; +} + +void DisplayServerMacOSEmbedded::window_get_edr_values(WindowID p_window, CGFloat *r_max_potential_edr_value, CGFloat *r_max_edr_value) const { + _THREAD_SAFE_METHOD_ + +#define SET_VAL(v, val) \ + if (v) { \ + *v = val; \ + } + + if (@available(macOS 10.15, *)) { + SET_VAL(r_max_potential_edr_value, state.screen_max_edr); + SET_VAL(r_max_edr_value, state.screen_max_potential_edr); + } else { + SET_VAL(r_max_potential_edr_value, 1.0); + SET_VAL(r_max_edr_value, 1.0); + } + +#undef SET_VAL +} + +void DisplayServerMacOSEmbedded::update_screen_parameters() { + if (hdr_output.requested) { + _update_hdr_output(MAIN_WINDOW_ID, hdr_output); + } +} + +void DisplayServerMacOSEmbedded::set_state(const DisplayServerMacOSEmbeddedState &p_state) { if (state == p_state) { return; } @@ -764,9 +724,12 @@ void DisplayServerEmbedded::set_state(const DisplayServerEmbeddedState &p_state) } #endif } + if (hdr_output.requested) { + _update_hdr_output(MAIN_WINDOW_ID, hdr_output); + } } -void DisplayServerEmbedded::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { +void DisplayServerMacOSEmbedded::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { #if defined(GLES3_ENABLED) if (gl_manager) { gl_manager->set_vsync_enabled(p_vsync_mode != DisplayServer::VSYNC_DISABLED); @@ -780,7 +743,7 @@ void DisplayServerEmbedded::window_set_vsync_mode(DisplayServer::VSyncMode p_vsy #endif } -DisplayServer::VSyncMode DisplayServerEmbedded::window_get_vsync_mode(WindowID p_window) const { +DisplayServer::VSyncMode DisplayServerMacOSEmbedded::window_get_vsync_mode(WindowID p_window) const { _THREAD_SAFE_METHOD_ #if defined(GLES3_ENABLED) if (gl_manager) { @@ -795,31 +758,12 @@ DisplayServer::VSyncMode DisplayServerEmbedded::window_get_vsync_mode(WindowID p return DisplayServer::VSYNC_ENABLED; } -void DisplayServerEmbedded::update_im_text(const Point2i &p_selection, const String &p_text) { - im_selection = p_selection; - im_text = p_text; - - OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE); -} - -Point2i DisplayServerEmbedded::ime_get_selection() const { - return im_selection; -} - -String DisplayServerEmbedded::ime_get_text() const { - return im_text; -} - -void DisplayServerEmbedded::cursor_set_shape(CursorShape p_shape) { +void DisplayServerMacOSEmbedded::cursor_set_shape(CursorShape p_shape) { cursor_shape = p_shape; EngineDebugger::get_singleton()->send_message("game_view:cursor_set_shape", { p_shape }); } -DisplayServer::CursorShape DisplayServerEmbedded::cursor_get_shape() const { - return cursor_shape; -} - -void DisplayServerEmbedded::cursor_set_custom_image(const Ref &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { +void DisplayServerMacOSEmbedded::cursor_set_custom_image(const Ref &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { PackedByteArray data; if (p_cursor.is_valid()) { Ref image = _get_cursor_image_from_resource(p_cursor, p_hotspot); @@ -830,7 +774,7 @@ void DisplayServerEmbedded::cursor_set_custom_image(const Ref &p_curso EngineDebugger::get_singleton()->send_message("game_view:cursor_set_custom_image", { data, p_shape, p_hotspot }); } -void DisplayServerEmbedded::swap_buffers() { +void DisplayServerMacOSEmbedded::swap_buffers() { #ifdef GLES3_ENABLED if (gl_manager) { gl_manager->swap_buffers(); @@ -838,20 +782,22 @@ void DisplayServerEmbedded::swap_buffers() { #endif } -void DisplayServerEmbeddedState::serialize(PackedByteArray &r_data) { - r_data.resize(16); +void DisplayServerMacOSEmbeddedState::serialize(PackedByteArray &r_data) { + r_data.resize(32); uint8_t *data = r_data.ptrw(); data += encode_float(screen_max_scale, data); data += encode_float(screen_dpi, data); data += encode_float(screen_window_scale, data); data += encode_uint32(display_id, data); + data += encode_double(screen_max_edr, data); + data += encode_double(screen_max_potential_edr, data); // Assert we had enough space. DEV_ASSERT(r_data.size() >= (data - r_data.ptrw())); } -Error DisplayServerEmbeddedState::deserialize(const PackedByteArray &p_data) { +Error DisplayServerMacOSEmbeddedState::deserialize(const PackedByteArray &p_data) { const uint8_t *data = p_data.ptr(); screen_max_scale = decode_float(data); @@ -861,6 +807,10 @@ Error DisplayServerEmbeddedState::deserialize(const PackedByteArray &p_data) { screen_window_scale = decode_float(data); data += sizeof(float); display_id = decode_uint32(data); + data += sizeof(uint32_t); + screen_max_edr = decode_double(data); + data += sizeof(double); + screen_max_potential_edr = decode_double(data); return OK; } diff --git a/platform/macos/editor/embedded_process_macos.mm b/platform/macos/editor/embedded_process_macos.mm index 962c2755e3..c570774302 100644 --- a/platform/macos/editor/embedded_process_macos.mm +++ b/platform/macos/editor/embedded_process_macos.mm @@ -30,8 +30,8 @@ #include "embedded_process_macos.h" -#include "platform/macos/display_server_embedded.h" #include "platform/macos/display_server_macos.h" +#include "platform/macos/display_server_macos_embedded.h" #include "core/input/input_event_codec.h" #include "core/os/main_loop.h" @@ -127,13 +127,18 @@ void EmbeddedProcessMacOS::request_close() { } void EmbeddedProcessMacOS::display_state_changed() { - DisplayServerEmbeddedState state; + DisplayServerMacOSEmbeddedState state; state.screen_max_scale = ds->screen_get_max_scale(); state.screen_dpi = ds->screen_get_dpi(); DisplayServer::WindowID wid = window->get_window_id(); state.screen_window_scale = ds->screen_get_scale(ds->window_get_current_screen(wid)); state.display_id = ds->window_get_display_id(wid); + CGFloat max_edr, max_potential_edr; + ds->window_get_edr_values(wid, &max_edr, &max_potential_edr); + state.screen_max_edr = max_edr; + state.screen_max_potential_edr = max_potential_edr; + PackedByteArray data; state.serialize(data); script_debugger->send_message("embed:ds_state", { data }); diff --git a/platform/macos/embedded_debugger.h b/platform/macos/embedded_debugger.h index 74217b2f20..37f06c2e16 100644 --- a/platform/macos/embedded_debugger.h +++ b/platform/macos/embedded_debugger.h @@ -33,23 +33,23 @@ #include "core/templates/hash_map.h" #include "core/variant/array.h" -class DisplayServerEmbedded; +class DisplayServerMacOSEmbedded; /// @brief Singleton class to process embedded debugging message in the child process. class EmbeddedDebugger { inline static EmbeddedDebugger *singleton = nullptr; - EmbeddedDebugger(DisplayServerEmbedded *p_ds); + EmbeddedDebugger(DisplayServerMacOSEmbedded *p_ds); public: - static void initialize(DisplayServerEmbedded *p_ds); + static void initialize(DisplayServerMacOSEmbedded *p_ds); static void deinitialize(); ~EmbeddedDebugger(); #ifdef DEBUG_ENABLED private: - DisplayServerEmbedded *ds; + DisplayServerMacOSEmbedded *ds; /// Message handler function for parse_message. typedef Error (EmbeddedDebugger::*ParseMessageFunc)(const Array &p_args); diff --git a/platform/macos/embedded_debugger.mm b/platform/macos/embedded_debugger.mm index 4896339905..7f79fd5dc5 100644 --- a/platform/macos/embedded_debugger.mm +++ b/platform/macos/embedded_debugger.mm @@ -30,7 +30,7 @@ #include "embedded_debugger.h" -#include "display_server_embedded.h" +#include "display_server_macos_embedded.h" #include "core/debugger/engine_debugger.h" #include "core/input/input.h" @@ -42,7 +42,7 @@ HashMap EmbeddedDebugger::parse_message_handlers; #endif -EmbeddedDebugger::EmbeddedDebugger(DisplayServerEmbedded *p_ds) { +EmbeddedDebugger::EmbeddedDebugger(DisplayServerMacOSEmbedded *p_ds) { singleton = this; #ifdef DEBUG_ENABLED @@ -58,7 +58,7 @@ EmbeddedDebugger::~EmbeddedDebugger() { singleton = nullptr; } -void EmbeddedDebugger::initialize(DisplayServerEmbedded *p_ds) { +void EmbeddedDebugger::initialize(DisplayServerMacOSEmbedded *p_ds) { if (EngineDebugger::is_active()) { memnew(EmbeddedDebugger(p_ds)); } @@ -165,7 +165,7 @@ Error EmbeddedDebugger::_msg_notification(const Array &p_args) { Error EmbeddedDebugger::_msg_ds_state(const Array &p_args) { ERR_FAIL_COND_V_MSG(p_args.size() != 1, ERR_INVALID_PARAMETER, "Invalid number of arguments for 'ds_state' message."); PackedByteArray data = p_args[0]; - DisplayServerEmbeddedState state; + DisplayServerMacOSEmbeddedState state; state.deserialize(data); ds->set_state(state); return OK; diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm index d816cb8d69..fb3cb66c8d 100644 --- a/platform/macos/os_macos.mm +++ b/platform/macos/os_macos.mm @@ -32,7 +32,7 @@ #import "dir_access_macos.h" #ifdef TOOLS_ENABLED -#import "display_server_embedded.h" +#import "display_server_macos_embedded.h" #endif #import "display_server_macos.h" #import "godot_application.h" @@ -1282,9 +1282,9 @@ void OS_MacOS_Embedded::run() { ret = Main::start(); } - DisplayServerEmbedded *ds = Object::cast_to(DisplayServer::get_singleton()); + DisplayServerMacOSEmbedded *ds = Object::cast_to(DisplayServer::get_singleton()); if (!ds) { - ERR_FAIL_MSG("DisplayServerEmbedded is not initialized."); + ERR_FAIL_MSG("DisplayServerMacOSEmbedded is not initialized."); } if (ds && ret == EXIT_SUCCESS && main_loop) { @@ -1324,7 +1324,7 @@ void OS_MacOS_Embedded::run() { OS_MacOS_Embedded::OS_MacOS_Embedded(const char *p_execpath, int p_argc, char **p_argv) : OS_MacOS(p_execpath, p_argc, p_argv) { - DisplayServerEmbedded::register_embedded_driver(); + DisplayServerMacOSEmbedded::register_embedded_driver(); } #endif diff --git a/platform/visionos/display_server_visionos.h b/platform/visionos/display_server_visionos.h index 56fbe4e5cc..a3e01bcca0 100644 --- a/platform/visionos/display_server_visionos.h +++ b/platform/visionos/display_server_visionos.h @@ -51,4 +51,9 @@ public: virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override; + +protected: + virtual bool _screen_hdr_is_supported() const override; + virtual float _screen_potential_edr_headroom() const override; + virtual float _screen_current_edr_headroom() const override; }; diff --git a/platform/visionos/display_server_visionos.mm b/platform/visionos/display_server_visionos.mm index 76c6938d51..eee22bd983 100644 --- a/platform/visionos/display_server_visionos.mm +++ b/platform/visionos/display_server_visionos.mm @@ -77,3 +77,19 @@ float DisplayServerVisionOS::screen_get_scale(int p_screen) const { return 1; } + +bool DisplayServerVisionOS::_screen_hdr_is_supported() const { + return true; +} + +float DisplayServerVisionOS::_screen_potential_edr_headroom() const { + // Current Apple Vision Pro hardware supports an EDR headroom of 2.0 (two times the SDR range). + // See https://developer.apple.com/videos/play/wwdc2023/10089?time=603 + return 2.0f; +} + +float DisplayServerVisionOS::_screen_current_edr_headroom() const { + // Current Apple Vision Pro hardware supports an EDR headroom of 2.0 (two times the SDR range). + // See https://developer.apple.com/videos/play/wwdc2023/10089?time=603 + return 2.0f; +}