Commit d9094421e16594438f248ac73491b7a75224ad77

Alex Szpakowski 2018-10-12T17:55:42

metal: Fix high dpi and resizing on macOS, and clean up iOS code. Fixes bug #4250.

diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m
index 10c585b..1fc0a4f 100644
--- a/src/render/metal/SDL_render_metal.m
+++ b/src/render/metal/SDL_render_metal.m
@@ -752,11 +752,6 @@ METAL_ActivateRenderCommandEncoder(SDL_Renderer * renderer, MTLLoadAction load)
 static void
 METAL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
 {
-    if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) {
-        METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
-        data.mtllayer.drawableSize = CGSizeMake(event->data1, event->data2);
-    }
-
     if (event->event == SDL_WINDOWEVENT_SHOWN ||
         event->event == SDL_WINDOWEVENT_HIDDEN) {
         // !!! FIXME: write me
@@ -848,12 +843,20 @@ METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
         mtltexdesc.height = (texture->h + 1) / 2;
         mtltexdesc.textureType = MTLTextureType2DArray;
         mtltexdesc.arrayLength = 2;
-        mtltexture_uv = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
     } else if (nv12) {
         mtltexdesc.pixelFormat = MTLPixelFormatRG8Unorm;
         mtltexdesc.width = (texture->w + 1) / 2;
         mtltexdesc.height = (texture->h + 1) / 2;
+    }
+
+    if (yuv || nv12) {
         mtltexture_uv = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
+        if (mtltexture_uv == nil) {
+#if !__has_feature(objc_arc)
+            [mtltexture release];
+#endif
+            return SDL_SetError("Texture allocation failed");
+        }
     }
 
     METAL_TextureData *texturedata = [[METAL_TextureData alloc] init];
diff --git a/src/video/cocoa/SDL_cocoametalview.h b/src/video/cocoa/SDL_cocoametalview.h
index c0a582f..185d45d 100644
--- a/src/video/cocoa/SDL_cocoametalview.h
+++ b/src/video/cocoa/SDL_cocoametalview.h
@@ -39,16 +39,16 @@
 
 #define METALVIEW_TAG 255
 
-@interface SDL_cocoametalview : NSView {
-    NSInteger _tag;
-}
+@interface SDL_cocoametalview : NSView
 
 - (instancetype)initWithFrame:(NSRect)frame
-                        scale:(CGFloat)scale;
+                      highDPI:(BOOL)highDPI;
 
 /* Override superclass tag so this class can set it. */
 @property (assign, readonly) NSInteger tag;
 
+@property (nonatomic) BOOL highDPI;
+
 @end
 
 SDL_cocoametalview* Cocoa_Mtl_AddMetalView(SDL_Window* window);
diff --git a/src/video/cocoa/SDL_cocoametalview.m b/src/video/cocoa/SDL_cocoametalview.m
index 8b7a697..9447fb8 100644
--- a/src/video/cocoa/SDL_cocoametalview.m
+++ b/src/video/cocoa/SDL_cocoametalview.m
@@ -33,9 +33,6 @@
 
 @implementation SDL_cocoametalview
 
-/* The synthesized getter should be called by super's viewWithTag. */
-@synthesize tag = _tag;
-
 /* Return a Metal-compatible layer. */
 + (Class)layerClass
 {
@@ -57,27 +54,48 @@
 }
 
 - (instancetype)initWithFrame:(NSRect)frame
-                        scale:(CGFloat)scale
+                      highDPI:(BOOL)highDPI
 {
     if ((self = [super initWithFrame:frame])) {
-        _tag = METALVIEW_TAG;
+        self.highDPI = highDPI;
         self.wantsLayer = YES;
 
         /* Allow resize. */
         self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
 
-        /* Set the desired scale. */
-        ((CAMetalLayer *) self.layer).drawableSize = NSSizeToCGSize([self bounds].size);
-        self.layer.contentsScale = scale;
+        [self updateDrawableSize];
     }
   
     return self;
 }
 
+- (NSInteger)tag
+{
+    return METALVIEW_TAG;
+}
+
+- (void)updateDrawableSize
+{
+    CAMetalLayer *metalLayer = (CAMetalLayer *)self.layer;
+    CGSize size = self.bounds.size;
+    CGSize backingSize = size;
+
+    if (self.highDPI) {
+        /* Note: NSHighResolutionCapable must be set to true in the app's
+         * Info.plist in order for the backing size to be high res.
+         */
+        backingSize = [self convertSizeToBacking:size];
+    }
+
+    metalLayer.contentsScale = backingSize.height / size.height;
+    metalLayer.drawableSize = backingSize;
+}
+
 /* Set the size of the metal drawables when the view is resized. */
 - (void)resizeWithOldSuperviewSize:(NSSize)oldSize
 {
     [super resizeWithOldSuperviewSize:oldSize];
+    [self updateDrawableSize];
 }
 
 @end
@@ -87,24 +105,10 @@ Cocoa_Mtl_AddMetalView(SDL_Window* window)
 {
     SDL_WindowData* data = (__bridge SDL_WindowData *)window->driverdata;
     NSView *view = data->nswindow.contentView;
-    CGFloat scale = 1.0;
-
-    if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
-        /* Set the scale to the natural scale factor of the screen - then
-         * the backing dimensions of the Metal view will match the pixel
-         * dimensions of the screen rather than the dimensions in points
-         * yielding high resolution on retine displays.
-         *
-         * N.B. In order for backingScaleFactor to be > 1,
-         * NSHighResolutionCapable must be set to true in the app's Info.plist.
-         */
-        NSWindow* nswindow = data->nswindow;
-        if ([nswindow.screen respondsToSelector:@selector(backingScaleFactor)])
-            scale = data->nswindow.screen.backingScaleFactor;
-    }
-        
-    SDL_cocoametalview *metalview
-        = [[SDL_cocoametalview alloc] initWithFrame:view.frame scale:scale];
+    BOOL highDPI = (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) != 0;
+    SDL_cocoametalview *metalview;
+
+    metalview = [[SDL_cocoametalview alloc] initWithFrame:view.frame highDPI:highDPI];
     [view addSubview:metalview];
     return metalview;
 }
diff --git a/src/video/uikit/SDL_uikitmetalview.m b/src/video/uikit/SDL_uikitmetalview.m
index fd94300..436e742 100644
--- a/src/video/uikit/SDL_uikitmetalview.m
+++ b/src/video/uikit/SDL_uikitmetalview.m
@@ -49,9 +49,8 @@
 {
     if ((self = [super initWithFrame:frame])) {
         self.tag = METALVIEW_TAG;
-        /* Set the desired scale. */
-        ((CAMetalLayer *) self.layer).drawableSize = self.bounds.size;
         self.layer.contentsScale = scale;
+        [self updateDrawableSize];
     }
 
     return self;
@@ -60,14 +59,16 @@
 /* Set the size of the metal drawables when the view is resized. */
 - (void)layoutSubviews
 {
-    CGSize bounds;
-
     [super layoutSubviews];
+    [self updateDrawableSize];
+}
 
-    bounds = [self bounds].size;
-    bounds.width *= self.layer.contentsScale;
-    bounds.height *= self.layer.contentsScale;
-    ((CAMetalLayer *) self.layer).drawableSize = bounds;
+- (void)updateDrawableSize
+{
+    CGSize size = self.bounds.size;
+    size.width *= self.layer.contentsScale;
+    size.height *= self.layer.contentsScale;
+    ((CAMetalLayer *)self.layer).drawableSize = size;
 }
 
 @end