Browse Source

[SDL3] macOS SetCursor performance fix (fixes #7151) (#7249)

* Setting the same mouse cursor twice is a no-op

* Cocoa: Call [NSCursor set] to change mouse cursor

The previous way, changing the mouse cursor was handled by invalidating
the mouse cursor rectangles and then recreating them (with the new
cursor) the next event loop. This is extremely slow; sometimes it can
take over a millisecond! With [NSCursor set] it happens instantly and
very quick performance-wise.

The downside is that it sets the cursor for the whole screen, so we
have some guards in place to change it to the system cursor if
the mouse moves outside the window or the window loses focus.

* Cocoa: Remove unneeded resetCursorRects: function
Sean Ridenour 2 years ago
parent
commit
a077cc8e4d
3 changed files with 15 additions and 20 deletions
  1. 5 0
      src/events/SDL_mouse.c
  2. 9 5
      src/video/cocoa/SDL_cocoamouse.m
  3. 1 15
      src/video/cocoa/SDL_cocoawindow.m

+ 5 - 0
src/events/SDL_mouse.c

@@ -1260,6 +1260,11 @@ void SDL_SetCursor(SDL_Cursor *cursor)
 {
     SDL_Mouse *mouse = SDL_GetMouse();
 
+    /* Return immediately if setting the cursor to the currently set one (fixes #7151) */
+    if (cursor == mouse->cur_cursor) {
+        return;
+    }
+
     /* Set the new cursor */
     if (cursor) {
         /* Make sure the cursor is still valid for this mouse */

+ 9 - 5
src/video/cocoa/SDL_cocoamouse.m

@@ -219,11 +219,15 @@ static int Cocoa_ShowCursor(SDL_Cursor *cursor)
         SDL_VideoDevice *device = SDL_GetVideoDevice();
         SDL_Window *window = (device ? device->windows : NULL);
         for (; window != NULL; window = window->next) {
-            SDL_WindowData *driverdata = window->driverdata;
-            if (driverdata) {
-                [driverdata.nswindow performSelectorOnMainThread:@selector(invalidateCursorRectsForView:)
-                                                      withObject:[driverdata.nswindow contentView]
-                                                   waitUntilDone:NO];
+            SDL_Mouse *mouse = SDL_GetMouse();
+            if(mouse->focus) {
+                if (mouse->cursor_shown && mouse->cur_cursor && !mouse->relative_mode) {
+                    [(__bridge NSCursor*)mouse->cur_cursor->driverdata set];
+                } else {
+                    [[NSCursor invisibleCursor] set];
+                }
+            } else {
+                [[NSCursor arrowCursor] set];
             }
         }
         return 0;

+ 1 - 15
src/video/cocoa/SDL_cocoawindow.m

@@ -1590,21 +1590,6 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse *mouse, NSEvent *theEvent, SDL_
     return YES;
 }
 
-- (void)resetCursorRects
-{
-    SDL_Mouse *mouse;
-    [super resetCursorRects];
-    mouse = SDL_GetMouse();
-
-    if (mouse->cursor_shown && mouse->cur_cursor && !mouse->relative_mode) {
-        [self addCursorRect:[self bounds]
-                     cursor:(__bridge NSCursor *)mouse->cur_cursor->driverdata];
-    } else {
-        [self addCursorRect:[self bounds]
-                     cursor:[NSCursor invisibleCursor]];
-    }
-}
-
 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
 {
     if (SDL_GetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH)) {
@@ -1613,6 +1598,7 @@ static int Cocoa_SendMouseButtonClicks(SDL_Mouse *mouse, NSEvent *theEvent, SDL_
         return SDL_GetHintBoolean("SDL_MAC_MOUSE_FOCUS_CLICKTHROUGH", SDL_FALSE);
     }
 }
+
 @end
 
 static int SetupWindowData(_THIS, SDL_Window *window, NSWindow *nswindow, NSView *nsview, SDL_bool created)