SDL: Don't recreate the window when creating a Metal renderer on an OpenGL window.

From 70656b133c89c614422130b6c7e0a52cdccc6d4f Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Tue, 15 Nov 2022 10:18:41 -0800
Subject: [PATCH] Don't recreate the window when creating a Metal renderer on
 an OpenGL window.

It turns out that we can safely create a Metal view on an existing window, and that avoids issues with the window being recreated with the wrong orientation in iOS 16.

Fixes https://github.com/libsdl-org/SDL/issues/6289
---
 src/render/metal/SDL_render_metal.m  | 25 -------------------------
 src/video/SDL_video.c                | 12 ++++++++++--
 src/video/uikit/SDL_uikitmetalview.m | 12 ------------
 src/video/uikit/SDL_uikitview.h      |  2 --
 src/video/uikit/SDL_uikitview.m      | 25 +------------------------
 5 files changed, 11 insertions(+), 65 deletions(-)

diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m
index 3dc346d57e45..80fd96f07542 100644
--- a/src/render/metal/SDL_render_metal.m
+++ b/src/render/metal/SDL_render_metal.m
@@ -55,9 +55,6 @@
 
 /* Apple Metal renderer implementation */
 
-/* Used to re-create the window with Metal capability */
-extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags);
-
 /* macOS requires constants in a buffer to have a 256 byte alignment. */
 /* Use native type alignments from https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf */
 #if defined(__MACOSX__) || TARGET_OS_SIMULATOR || TARGET_OS_MACCATALYST
@@ -1635,13 +1632,11 @@ static SDL_MetalView GetWindowView(SDL_Window *window)
     SDL_MetalView view = NULL;
     CAMetalLayer *layer = nil;
     SDL_SysWMinfo syswm;
-    SDL_bool changed_window = SDL_FALSE;
     NSError *err = nil;
     dispatch_data_t mtllibdata;
     char *constantdata;
     int maxtexsize, quadcount = UINT16_MAX / 4;
     UInt16 *indexdata;
-    Uint32 window_flags;
     size_t indicessize = sizeof(UInt16) * quadcount * 6;
     MTLSamplerDescriptor *samplerdesc;
     id<MTLCommandQueue> mtlcmdqueue;
@@ -1697,20 +1692,9 @@ static SDL_MetalView GetWindowView(SDL_Window *window)
         return NULL;
     }
 
-    window_flags = SDL_GetWindowFlags(window);
-    if (!(window_flags & SDL_WINDOW_METAL)) {
-        changed_window = SDL_TRUE;
-        if (SDL_RecreateWindow(window, (window_flags & ~(SDL_WINDOW_VULKAN | SDL_WINDOW_OPENGL)) | SDL_WINDOW_METAL) < 0) {
-            return NULL;
-        }
-    }
-
     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
     if (!renderer) {
         SDL_OutOfMemory();
-        if (changed_window) {
-            SDL_RecreateWindow(window, window_flags);
-        }
         return NULL;
     }
 
@@ -1720,9 +1704,6 @@ static SDL_MetalView GetWindowView(SDL_Window *window)
     if (mtldevice == nil) {
         SDL_free(renderer);
         SDL_SetError("Failed to obtain Metal device");
-        if (changed_window) {
-            SDL_RecreateWindow(window, window_flags);
-        }
         return NULL;
     }
 
@@ -1733,9 +1714,6 @@ static SDL_MetalView GetWindowView(SDL_Window *window)
 
     if (view == NULL) {
         SDL_free(renderer);
-        if (changed_window) {
-            SDL_RecreateWindow(window, window_flags);
-        }
         return NULL;
     }
 
@@ -1749,9 +1727,6 @@ in case we want to use it later (recreating the renderer)
         /* SDL_Metal_DestroyView(view); */
         CFBridgingRelease(view);
         SDL_free(renderer);
-        if (changed_window) {
-            SDL_RecreateWindow(window, window_flags);
-        }
         return NULL;
     }
 
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 51e282f066c5..c75b026d981a 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -4853,8 +4853,16 @@ SDL_Metal_CreateView(SDL_Window * window)
     CHECK_WINDOW_MAGIC(window, NULL);
 
     if (!(window->flags & SDL_WINDOW_METAL)) {
-        SDL_SetError("The specified window isn't a Metal window");
-        return NULL;
+        /* No problem, we can convert to Metal */
+        if (window->flags & SDL_WINDOW_OPENGL) {
+            window->flags &= ~SDL_WINDOW_OPENGL;
+            SDL_GL_UnloadLibrary();
+        }
+        if (window->flags & SDL_WINDOW_VULKAN) {
+            window->flags &= ~SDL_WINDOW_VULKAN;
+            SDL_Vulkan_UnloadLibrary();
+        }
+        window->flags |= SDL_WINDOW_METAL;
     }
 
     return _this->Metal_CreateView(_this, window);
diff --git a/src/video/uikit/SDL_uikitmetalview.m b/src/video/uikit/SDL_uikitmetalview.m
index af6eb0cd3ebe..8bc3380955e8 100644
--- a/src/video/uikit/SDL_uikitmetalview.m
+++ b/src/video/uikit/SDL_uikitmetalview.m
@@ -69,18 +69,6 @@ - (void)updateDrawableSize
     CGSize size = self.bounds.size;
     size.width *= self.layer.contentsScale;
     size.height *= self.layer.contentsScale;
-
-    /* Make sure the width/height are oriented correctly
-     *
-     * This works around an issue in iOS 16 where the bounds come back in portrait mode
-     * instead of landscape until the event loop runs.
-     */
-    if ([self shouldSwapDimensions:(size.width >= size.height)]) {
-        CGFloat temp = size.width;
-        size.width = size.height;
-        size.height = temp;
-    }
-
     ((CAMetalLayer *)self.layer).drawableSize = size;
 }
 
diff --git a/src/video/uikit/SDL_uikitview.h b/src/video/uikit/SDL_uikitview.h
index 5369bb212455..dcd63c7a5645 100644
--- a/src/video/uikit/SDL_uikitview.h
+++ b/src/video/uikit/SDL_uikitview.h
@@ -35,8 +35,6 @@
 
 - (void)setSDLWindow:(SDL_Window *)window;
 
-- (BOOL)shouldSwapDimensions:(BOOL)portrait;
-
 #if !TARGET_OS_TV && defined(__IPHONE_13_4)
 - (UIPointerRegion *)pointerInteraction:(UIPointerInteraction *)interaction regionForRequest:(UIPointerRegionRequest *)request defaultRegion:(UIPointerRegion *)defaultRegion API_AVAILABLE(ios(13.4));
 - (UIPointerStyle *)pointerInteraction:(UIPointerInteraction *)interaction styleForRegion:(UIPointerRegion *)region  API_AVAILABLE(ios(13.4));
diff --git a/src/video/uikit/SDL_uikitview.m b/src/video/uikit/SDL_uikitview.m
index 50d7393e7f30..8c48cfa503b9 100644
--- a/src/video/uikit/SDL_uikitview.m
+++ b/src/video/uikit/SDL_uikitview.m
@@ -120,8 +120,6 @@ - (void)setSDLWindow:(SDL_Window *)window
         [data.uiwindow layoutIfNeeded];
     }
 
-    sdlwindow = window;
-
     /* Add ourself to the new window. */
     if (window) {
         data = (__bridge SDL_WindowData *) window->driverdata;
@@ -146,29 +144,8 @@ - (void)setSDLWindow:(SDL_Window *)window
          * layout now to immediately update the bounds. */
         [data.uiwindow layoutIfNeeded];
     }
-}
-
-- (BOOL)shouldSwapDimensions:(BOOL)landscape
-{
-#if !TARGET_OS_TV
-    if (sdlwindow) {
-        SDL_VideoDisplay *display = SDL_GetDisplayForWindow(sdlwindow);
-        SDL_DisplayData *displaydata = (__bridge SDL_DisplayData *) display->driverdata;
 
-        if (displaydata.uiscreen == [UIScreen mainScreen]) {
-            NSUInteger orients = UIKit_GetSupportedOrientations(sdlwindow);
-            BOOL supportsLandscape = (orients & UIInterfaceOrientationMaskLandscape) != 0;
-            BOOL supportsPortrait = (orients & (UIInterfaceOrientationMaskPortrait|UIInterfaceOrientationMaskPortraitUpsideDown)) != 0;
-
-            /* Make sure the width/height are oriented correctly */
-            if ((landscape && !supportsLandscape) || (!landscape && !supportsPortrait)) {
-                return YES;
-            }
-        }
-    }
-#endif /* !TARGET_OS_TV */
-
-    return NO;
+    sdlwindow = window;
 }
 
 #if !TARGET_OS_TV && defined(__IPHONE_13_4)