diff --git a/platform/macos/display_server_embedded.h b/platform/macos/display_server_embedded.h index 90851df487b..598118abc18 100644 --- a/platform/macos/display_server_embedded.h +++ b/platform/macos/display_server_embedded.h @@ -43,9 +43,15 @@ struct DisplayServerEmbeddedState { /// Default to a scale of 2.0, which is the most common. float screen_max_scale = 2.0f; float screen_dpi = 96.0f; + /// The display ID of the window which is displaying the the embedded process content. + uint32_t display_id = -1; 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; + } }; class DisplayServerEmbedded : public DisplayServer { diff --git a/platform/macos/display_server_embedded.mm b/platform/macos/display_server_embedded.mm index 1b8d3d37d31..2b05e344fa4 100644 --- a/platform/macos/display_server_embedded.mm +++ b/platform/macos/display_server_embedded.mm @@ -135,6 +135,7 @@ DisplayServerEmbedded::DisplayServerEmbedded(const String &p_rendering_driver, W if (err != OK) { ERR_FAIL_MSG("Could not create OpenGL context."); } + gl_manager->set_vsync_enabled(p_vsync_mode != DisplayServer::VSYNC_DISABLED); } #endif @@ -708,15 +709,44 @@ void DisplayServerEmbedded::window_set_ime_position(const Point2i &p_pos, Window } void DisplayServerEmbedded::set_state(const DisplayServerEmbeddedState &p_state) { + if (state == p_state) { + return; + } + + uint32_t old_display_id = state.display_id; + state = p_state; + + if (state.display_id != old_display_id) { +#if defined(GLES3_ENABLED) + if (gl_manager) { + gl_manager->set_display_id(state.display_id); + } +#endif + } } void DisplayServerEmbedded::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) { - // Not supported +#if defined(GLES3_ENABLED) + if (gl_manager) { + gl_manager->set_vsync_enabled(p_vsync_mode != DisplayServer::VSYNC_DISABLED); + } +#endif + +#if defined(RD_ENABLED) + if (rendering_context) { + rendering_context->window_set_vsync_mode(p_window, p_vsync_mode); + } +#endif } DisplayServer::VSyncMode DisplayServerEmbedded::window_get_vsync_mode(WindowID p_window) const { _THREAD_SAFE_METHOD_ +#if defined(GLES3_ENABLED) + if (gl_manager) { + return (gl_manager->is_vsync_enabled() ? DisplayServer::VSyncMode::VSYNC_ENABLED : DisplayServer::VSyncMode::VSYNC_DISABLED); + } +#endif #if defined(RD_ENABLED) if (rendering_context) { return rendering_context->window_get_vsync_mode(p_window); @@ -762,14 +792,15 @@ void DisplayServerEmbedded::swap_buffers() { } void DisplayServerEmbeddedState::serialize(PackedByteArray &r_data) { - r_data.resize(8); + r_data.resize(12); uint8_t *data = r_data.ptrw(); data += encode_float(screen_max_scale, data); data += encode_float(screen_dpi, data); + data += encode_uint32(display_id, data); // Assert we had enough space. - DEV_ASSERT(data - r_data.ptrw() >= r_data.size()); + DEV_ASSERT((data - r_data.ptrw()) >= r_data.size()); } Error DisplayServerEmbeddedState::deserialize(const PackedByteArray &p_data) { @@ -778,6 +809,8 @@ Error DisplayServerEmbeddedState::deserialize(const PackedByteArray &p_data) { screen_max_scale = decode_float(data); data += sizeof(float); screen_dpi = decode_float(data); + data += sizeof(float); + display_id = decode_uint32(data); return OK; } diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index f6c7309b3c5..72e08ceb9c5 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -101,6 +101,8 @@ public: Vector mpath; + CGDirectDisplayID display_id = -1; + Point2i mouse_pos; WindowResizeEdge edge = WINDOW_EDGE_MAX; @@ -253,10 +255,12 @@ private: void initialize_tts() const; struct EmbeddedProcessData { - const EmbeddedProcessMacOS *process; + EmbeddedProcessMacOS *process; + WindowData *wd = nullptr; CALayer *layer_host = nil; }; HashMap embedded_processes; + void _window_update_display_id(WindowData *p_wd); public: void menu_callback(id p_sender); @@ -294,6 +298,10 @@ public: bool is_always_on_top_recursive(WindowID p_window) const; + /** + * Get the display ID of a window. + */ + uint32_t window_get_display_id(WindowID p_window) const; void window_destroy(WindowID p_window); void window_resize(WindowID p_window, int p_width, int p_height); void window_set_custom_window_buttons(WindowData &p_wd, bool p_enabled); @@ -461,7 +469,7 @@ public: virtual void enable_for_stealing_focus(OS::ProcessID pid) override; #ifdef TOOLS_ENABLED - Error embed_process_update(WindowID p_window, const EmbeddedProcessMacOS *p_process); + Error embed_process_update(WindowID p_window, EmbeddedProcessMacOS *p_process); #endif virtual Error request_close_embedded_process(OS::ProcessID p_pid) override; virtual Error remove_embedded_process(OS::ProcessID p_pid) override; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index ca4ca3454ac..9b1bbd626d3 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -54,6 +54,7 @@ #include "scene/resources/image_texture.h" #ifdef TOOLS_ENABLED +#import "display_server_embedded.h" #import "editor/embedded_process_macos.h" #endif @@ -87,7 +88,7 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod { WindowData &wd = windows[id]; - wd.window_delegate = [[GodotWindowDelegate alloc] init]; + wd.window_delegate = [[GodotWindowDelegate alloc] initWithDisplayServer:this]; ERR_FAIL_NULL_V_MSG(wd.window_delegate, INVALID_WINDOW_ID, "Can't create a window delegate"); [wd.window_delegate setWindowID:id]; @@ -2196,6 +2197,8 @@ void DisplayServerMacOS::reparent_check(WindowID p_window) { WindowData &wd = windows[p_window]; NSScreen *screen = [wd.window_object screen]; + _window_update_display_id(&wd); + if (wd.transient_parent != INVALID_WINDOW_ID) { WindowData &wd_parent = windows[wd.transient_parent]; NSScreen *parent_screen = [wd_parent.window_object screen]; @@ -3284,9 +3287,34 @@ void DisplayServerMacOS::enable_for_stealing_focus(OS::ProcessID pid) { ERR_FAIL_V(m_retval); \ } +uint32_t DisplayServerMacOS::window_get_display_id(WindowID p_window) const { + const WindowData *wd; + GET_OR_FAIL_V(wd, windows, p_window, -1); + return wd->display_id; +} + +void DisplayServerMacOS::_window_update_display_id(WindowData *p_wd) { + NSScreen *screen = [p_wd->window_object screen]; + CGDirectDisplayID display_id = [[screen deviceDescription][@"NSScreenNumber"] unsignedIntValue]; + if (p_wd->display_id == display_id) { + return; + } + + p_wd->display_id = display_id; + +#ifdef TOOLS_ENABLED + // Notify any embedded processes of the new display ID, so that they can potentially update their vsync. + for (KeyValue &E : embedded_processes) { + if (E.value.wd == p_wd) { + E.value.process->display_state_changed(); + } + } +#endif +} + #ifdef TOOLS_ENABLED -Error DisplayServerMacOS::embed_process_update(WindowID p_window, const EmbeddedProcessMacOS *p_process) { +Error DisplayServerMacOS::embed_process_update(WindowID p_window, EmbeddedProcessMacOS *p_process) { _THREAD_SAFE_METHOD_ WindowData *wd; @@ -3303,6 +3331,7 @@ Error DisplayServerMacOS::embed_process_update(WindowID p_window, const Embedded ed = &embedded_processes.insert(p_pid, EmbeddedProcessData())->value; ed->process = p_process; + ed->wd = wd; CALayerHost *host = [CALayerHost new]; uint32_t p_context_id = p_process->get_context_id(); diff --git a/platform/macos/editor/embedded_process_macos.h b/platform/macos/editor/embedded_process_macos.h index 0723c97510a..bd00766aade 100644 --- a/platform/macos/editor/embedded_process_macos.h +++ b/platform/macos/editor/embedded_process_macos.h @@ -79,7 +79,7 @@ class EmbeddedProcessMacOS final : public EmbeddedProcessBase { // Helper functions. void _try_embed_process(); - void update_embedded_process() const; + void update_embedded_process(); void _joy_connection_changed(int p_index, bool p_connected) const; protected: @@ -113,6 +113,8 @@ public: _FORCE_INLINE_ LayerHost *get_layer_host() const { return layer_host; } + void display_state_changed(); + // MARK: - Embedded process state _FORCE_INLINE_ DisplayServer::MouseMode get_mouse_mode() const { return mouse_mode; } diff --git a/platform/macos/editor/embedded_process_macos.mm b/platform/macos/editor/embedded_process_macos.mm index fd9be68f139..2400e326588 100644 --- a/platform/macos/editor/embedded_process_macos.mm +++ b/platform/macos/editor/embedded_process_macos.mm @@ -53,7 +53,7 @@ void EmbeddedProcessMacOS::_notification(int p_what) { } } -void EmbeddedProcessMacOS::update_embedded_process() const { +void EmbeddedProcessMacOS::update_embedded_process() { layer_host->set_rect(get_adjusted_embedded_window_rect(get_rect())); if (is_embedding_completed()) { ds->embed_process_update(window->get_window_id(), this); @@ -130,24 +130,28 @@ void EmbeddedProcessMacOS::request_close() { } } +void EmbeddedProcessMacOS::display_state_changed() { + DisplayServerEmbeddedState state; + state.screen_max_scale = ds->screen_get_max_scale(); + state.screen_dpi = ds->screen_get_dpi(); + state.display_id = ds->window_get_display_id(window->get_window_id()); + PackedByteArray data; + state.serialize(data); + script_debugger->send_message("embed:ds_state", { data }); +} + void EmbeddedProcessMacOS::_try_embed_process() { if (current_process_id == 0 || script_debugger == nullptr || context_id == 0) { return; } - Error err = ds->embed_process_update(window->get_window_id(), this); + DisplayServer::WindowID wid = window->get_window_id(); + Error err = ds->embed_process_update(wid, this); if (err == OK) { layer_host->set_rect(get_adjusted_embedded_window_rect(get_rect())); - // Replicate some of the DisplayServer state. - { - DisplayServerEmbeddedState state; - state.screen_max_scale = ds->screen_get_max_scale(); - state.screen_dpi = ds->screen_get_dpi(); - PackedByteArray data; - state.serialize(data); - script_debugger->send_message("embed:ds_state", { data }); - } + // Replicate important DisplayServer state. + display_state_changed(); Rect2i rect = get_screen_embedded_window_rect(); script_debugger->send_message("embed:window_size", { rect.size }); diff --git a/platform/macos/embedded_gl_manager.h b/platform/macos/embedded_gl_manager.h index 64e4fc18f73..50aa2620253 100644 --- a/platform/macos/embedded_gl_manager.h +++ b/platform/macos/embedded_gl_manager.h @@ -53,6 +53,9 @@ class GLManagerEmbedded { /// Triple-buffering is used to avoid stuttering. static constexpr uint32_t BUFFER_COUNT = 3; + // The display ID for which vsync is used. If this value is -1, vsync is disabled. + constexpr static uint32_t INVALID_DISPLAY_ID = static_cast(-1); + struct FrameBuffer { IOSurfaceRef surface = nullptr; unsigned int tex = 0; @@ -86,12 +89,25 @@ class GLManagerEmbedded { CGLTexImageIOSurface2DPtr CGLTexImageIOSurface2D = nullptr; CGLErrorStringPtr CGLErrorString = nullptr; + uint32_t display_id = INVALID_DISPLAY_ID; + CVDisplayLinkRef display_link; + bool vsync_enabled = false; + bool display_link_running = false; + dispatch_semaphore_t display_semaphore = nullptr; + + void create_display_link(); + void release_display_link(); + public: Error window_create(DisplayServer::WindowID p_window_id, CALayer *p_layer, int p_width, int p_height); void window_destroy(DisplayServer::WindowID p_window_id); void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height); Size2i window_get_size(DisplayServer::WindowID p_window_id) const; + void set_display_id(uint32_t p_display_id); + void set_vsync_enabled(bool p_enabled); + bool is_vsync_enabled() const { return vsync_enabled; } + void release_current(); void swap_buffers(); diff --git a/platform/macos/embedded_gl_manager.mm b/platform/macos/embedded_gl_manager.mm index 19d8f8ec810..8788bfabf08 100644 --- a/platform/macos/embedded_gl_manager.mm +++ b/platform/macos/embedded_gl_manager.mm @@ -237,6 +237,10 @@ void GLManagerEmbedded::swap_buffers() { } last_valid = true; + if (display_link_running) { + dispatch_semaphore_wait(display_semaphore, DISPATCH_TIME_FOREVER); + } + [CATransaction begin]; [CATransaction setDisableActions:YES]; win.layer.contents = (__bridge id)win.framebuffers[win.current_fb].surface; @@ -249,7 +253,65 @@ Error GLManagerEmbedded::initialize() { return framework_loaded ? OK : ERR_CANT_CREATE; } +void GLManagerEmbedded::create_display_link() { + DEV_ASSERT(display_link == nullptr); + + CVReturn err = CVDisplayLinkCreateWithCGDisplay(CGMainDisplayID(), &display_link); + ERR_FAIL_COND_MSG(err != kCVReturnSuccess, "Failed to create display link."); + + __block dispatch_semaphore_t local_semaphore = display_semaphore; + + CVDisplayLinkSetOutputHandler(display_link, ^CVReturn(CVDisplayLinkRef p_display_link, const CVTimeStamp *p_now, const CVTimeStamp *p_output_time, CVOptionFlags p_flags, CVOptionFlags *p_flags_out) { + dispatch_semaphore_signal(local_semaphore); + return kCVReturnSuccess; + }); +} + +void GLManagerEmbedded::release_display_link() { + DEV_ASSERT(display_link != nullptr); + if (CVDisplayLinkIsRunning(display_link)) { + CVDisplayLinkStop(display_link); + } + CVDisplayLinkRelease(display_link); + display_link = nullptr; +} + +void GLManagerEmbedded::set_display_id(uint32_t p_display_id) { + if (display_id == p_display_id) { + return; + } + + CVReturn err = CVDisplayLinkSetCurrentCGDisplay(display_link, static_cast(p_display_id)); + ERR_FAIL_COND_MSG(err != kCVReturnSuccess, "Failed to set display ID for display link."); +} + +void GLManagerEmbedded::set_vsync_enabled(bool p_enabled) { + if (p_enabled == vsync_enabled) { + return; + } + + vsync_enabled = p_enabled; + + if (vsync_enabled) { + if (!CVDisplayLinkIsRunning(display_link)) { + CVReturn err = CVDisplayLinkStart(display_link); + ERR_FAIL_COND_MSG(err != kCVReturnSuccess, "Failed to start display link."); + display_link_running = true; + } + } else { + if (CVDisplayLinkIsRunning(display_link)) { + CVReturn err = CVDisplayLinkStop(display_link); + ERR_FAIL_COND_MSG(err != kCVReturnSuccess, "Failed to stop display link."); + display_link_running = false; + } + } +} + GLManagerEmbedded::GLManagerEmbedded() { + display_semaphore = dispatch_semaphore_create(BUFFER_COUNT); + + create_display_link(); + NSBundle *framework = [NSBundle bundleWithIdentifier:@"com.apple.opengl"]; if ([framework load]) { void *library_handle = dlopen([framework.executablePath UTF8String], RTLD_NOW); @@ -263,6 +325,7 @@ GLManagerEmbedded::GLManagerEmbedded() { } GLManagerEmbedded::~GLManagerEmbedded() { + release_display_link(); release_current(); } diff --git a/platform/macos/godot_window_delegate.h b/platform/macos/godot_window_delegate.h index 44013d9acd6..f46127672cd 100644 --- a/platform/macos/godot_window_delegate.h +++ b/platform/macos/godot_window_delegate.h @@ -35,10 +35,12 @@ #import #import -@interface GodotWindowDelegate : NSObject { - DisplayServer::WindowID window_id; -} +class DisplayServerMacOS; + +@interface GodotWindowDelegate : NSObject - (void)setWindowID:(DisplayServer::WindowID)wid; +- (instancetype)initWithDisplayServer:(DisplayServerMacOS *)p_ds; + @end diff --git a/platform/macos/godot_window_delegate.mm b/platform/macos/godot_window_delegate.mm index 35585813dac..8c105c375d8 100644 --- a/platform/macos/godot_window_delegate.mm +++ b/platform/macos/godot_window_delegate.mm @@ -35,15 +35,25 @@ #import "godot_content_view.h" #import "godot_window.h" -@implementation GodotWindowDelegate +@implementation GodotWindowDelegate { + DisplayServer::WindowID window_id; + DisplayServerMacOS *ds; +} + +- (instancetype)initWithDisplayServer:(DisplayServerMacOS *)p_ds { + if (self = [super init]) { + ds = p_ds; + window_id = DisplayServerMacOS::INVALID_WINDOW_ID; + } + return self; +} - (void)setWindowID:(DisplayServer::WindowID)wid { window_id = wid; } - (BOOL)windowShouldClose:(id)sender { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { + if (!ds->has_window(window_id)) { return YES; } @@ -52,8 +62,7 @@ } - (void)windowWillClose:(NSNotification *)notification { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { + if (!ds->has_window(window_id)) { return; } @@ -73,8 +82,7 @@ } - (void)windowWillEnterFullScreen:(NSNotification *)notification { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { + if (!ds->has_window(window_id)) { return; } @@ -91,8 +99,7 @@ } - (void)windowDidFailToEnterFullScreen:(NSWindow *)window { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { + if (!ds->has_window(window_id)) { return; } @@ -101,8 +108,7 @@ } - (void)windowDidEnterFullScreen:(NSNotification *)notification { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { + if (!ds->has_window(window_id)) { return; } @@ -126,8 +132,7 @@ } - (void)windowWillExitFullScreen:(NSNotification *)notification { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { + if (!ds->has_window(window_id)) { return; } @@ -143,8 +148,7 @@ } - (void)windowDidFailToExitFullScreen:(NSWindow *)window { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { + if (!ds->has_window(window_id)) { return; } @@ -159,8 +163,7 @@ } - (void)windowDidExitFullScreen:(NSNotification *)notification { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { + if (!ds->has_window(window_id)) { return; } @@ -206,8 +209,7 @@ } - (void)windowDidChangeBackingProperties:(NSNotification *)notification { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { + if (!ds->has_window(window_id)) { return; } @@ -237,8 +239,7 @@ } - (void)windowWillStartLiveResize:(NSNotification *)notification { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (ds && ds->has_window(window_id)) { + if (ds->has_window(window_id)) { DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); wd.last_frame_rect = [wd.window_object frame]; ds->set_is_resizing(true); @@ -246,15 +247,11 @@ } - (void)windowDidEndLiveResize:(NSNotification *)notification { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (ds) { - ds->set_is_resizing(false); - } + ds->set_is_resizing(false); } - (void)windowDidResize:(NSNotification *)notification { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { + if (!ds->has_window(window_id)) { return; } @@ -277,8 +274,7 @@ } - (void)windowDidChangeScreen:(NSNotification *)notification { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { + if (!ds->has_window(window_id)) { return; } @@ -286,8 +282,7 @@ } - (void)windowDidMove:(NSNotification *)notification { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { + if (!ds->has_window(window_id)) { return; } @@ -300,8 +295,7 @@ } - (void)windowDidBecomeKey:(NSNotification *)notification { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { + if (!ds->has_window(window_id)) { return; } @@ -332,8 +326,7 @@ } - (void)windowDidResignKey:(NSNotification *)notification { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { + if (!ds->has_window(window_id)) { return; } @@ -352,8 +345,7 @@ } - (void)windowDidMiniaturize:(NSNotification *)notification { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { + if (!ds->has_window(window_id)) { return; } @@ -368,8 +360,7 @@ } - (void)windowDidDeminiaturize:(NSNotification *)notification { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { + if (!ds->has_window(window_id)) { return; } @@ -386,8 +377,7 @@ } - (void)windowDidChangeOcclusionState:(NSNotification *)notification { - DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); - if (!ds || !ds->has_window(window_id)) { + if (!ds->has_window(window_id)) { return; } DisplayServerMacOS::WindowData &wd = ds->get_window(window_id);