From 69958441be6c2bc482778fbfdef24f172973a4fe Mon Sep 17 00:00:00 2001 From: Mark Callow Date: Wed, 21 Feb 2018 09:58:21 -0800 Subject: [PATCH] Fix high-dpi support on macOS and simplify it and iOS variant. The detault drawableSize for a CAMetalLayer is its bounds x its scale. So it is sufficient to set the *layer's* scale to the desired value. --- include/SDL_video.h | 4 ++- include/SDL_vulkan.h | 3 ++ src/video/cocoa/SDL_cocoametalview.h | 3 +- src/video/cocoa/SDL_cocoametalview.m | 42 +++++++++++++++++----------- src/video/uikit/SDL_uikitmetalview.h | 3 +- src/video/uikit/SDL_uikitmetalview.m | 30 ++++++-------------- 6 files changed, 42 insertions(+), 43 deletions(-) diff --git a/include/SDL_video.h b/include/SDL_video.h index 28fc4c3a4..83f49faae 100644 --- a/include/SDL_video.h +++ b/include/SDL_video.h @@ -110,7 +110,9 @@ typedef enum SDL_WINDOW_MOUSE_FOCUS = 0x00000400, /**< window has mouse focus */ SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ), SDL_WINDOW_FOREIGN = 0x00000800, /**< window not created by SDL */ - SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000, /**< window should be created in high-DPI mode if supported */ + SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000, /**< window should be created in high-DPI mode if supported. + On macOS NSHighResolutionCapable must be set true in the + application's Info.plist for this to have any effect. */ SDL_WINDOW_MOUSE_CAPTURE = 0x00004000, /**< window has mouse captured (unrelated to INPUT_GRABBED) */ SDL_WINDOW_ALWAYS_ON_TOP = 0x00008000, /**< window should always be above others */ SDL_WINDOW_SKIP_TASKBAR = 0x00010000, /**< window should not be added to the taskbar */ diff --git a/include/SDL_vulkan.h b/include/SDL_vulkan.h index 803b5feef..d238e2236 100644 --- a/include/SDL_vulkan.h +++ b/include/SDL_vulkan.h @@ -240,6 +240,9 @@ extern DECLSPEC SDL_bool SDLCALL SDL_Vulkan_CreateSurface( * platform with high-DPI support (Apple calls this "Retina"), and not disabled * by the \c SDL_HINT_VIDEO_HIGHDPI_DISABLED hint. * + * \note On macOS high-DPI support must be enabled for an application by + * setting NSHighResolutionCapable to true in its Info.plist. + * * \sa SDL_GetWindowSize() * \sa SDL_CreateWindow() */ diff --git a/src/video/cocoa/SDL_cocoametalview.h b/src/video/cocoa/SDL_cocoametalview.h index 131dad503..c0a582ff4 100644 --- a/src/video/cocoa/SDL_cocoametalview.h +++ b/src/video/cocoa/SDL_cocoametalview.h @@ -41,11 +41,10 @@ @interface SDL_cocoametalview : NSView { NSInteger _tag; - bool _useHighDPI; } - (instancetype)initWithFrame:(NSRect)frame - useHighDPI:(bool)useHighDPI; + scale:(CGFloat)scale; /* Override superclass tag so this class can set it. */ @property (assign, readonly) NSInteger tag; diff --git a/src/video/cocoa/SDL_cocoametalview.m b/src/video/cocoa/SDL_cocoametalview.m index 43e2636f9..e9c08a007 100644 --- a/src/video/cocoa/SDL_cocoametalview.m +++ b/src/video/cocoa/SDL_cocoametalview.m @@ -57,17 +57,19 @@ } - (instancetype)initWithFrame:(NSRect)frame - useHighDPI:(bool)useHighDPI + scale:(CGFloat)scale { if ((self = [super initWithFrame:frame])) { + _tag = METALVIEW_TAG; self.wantsLayer = YES; /* Allow resize. */ self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; - _tag = METALVIEW_TAG; - _useHighDPI = useHighDPI; - [self updateDrawableSize]; + /* Set the desired scale. The default drawableSize of a CAMetalLayer + * is its bounds x its scale so nothing further needs to be done. + */ + self.layer.contentsScale = scale; } return self; @@ -77,16 +79,6 @@ - (void)resizeWithOldSuperviewSize:(NSSize)oldSize { [super resizeWithOldSuperviewSize:oldSize]; - [self updateDrawableSize]; -} - -- (void)updateDrawableSize -{ - NSRect bounds = [self bounds]; - if (_useHighDPI) { - bounds = [self convertRectToBacking:bounds]; - } - ((CAMetalLayer *) self.layer).drawableSize = NSSizeToCGSize(bounds.size); } @end @@ -94,12 +86,26 @@ SDL_cocoametalview* Cocoa_Mtl_AddMetalView(SDL_Window* window) { - SDL_WindowData *data = (SDL_WindowData *)window->driverdata; + 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 - useHighDPI:(window->flags & SDL_WINDOW_ALLOW_HIGHDPI)]; + = [[SDL_cocoametalview alloc] initWithFrame:view.frame scale:scale]; [view addSubview:metalview]; return metalview; } @@ -119,6 +125,8 @@ Cocoa_Mtl_GetDrawableSize(SDL_Window * window, int * w, int * h) if (h) { *h = layer.drawableSize.height; } + } else { + SDL_GetWindowSize(window, w, h); } } diff --git a/src/video/uikit/SDL_uikitmetalview.h b/src/video/uikit/SDL_uikitmetalview.h index 31fefd4eb..bc977781f 100644 --- a/src/video/uikit/SDL_uikitmetalview.h +++ b/src/video/uikit/SDL_uikitmetalview.h @@ -43,8 +43,7 @@ @interface SDL_uikitmetalview : SDL_uikitview - (instancetype)initWithFrame:(CGRect)frame - scale:(CGFloat)scale - tag:(int)tag; + scale:(CGFloat)scale; @end diff --git a/src/video/uikit/SDL_uikitmetalview.m b/src/video/uikit/SDL_uikitmetalview.m index 4ee8b9486..104189d8c 100644 --- a/src/video/uikit/SDL_uikitmetalview.m +++ b/src/video/uikit/SDL_uikitmetalview.m @@ -46,14 +46,12 @@ - (instancetype)initWithFrame:(CGRect)frame scale:(CGFloat)scale - tag:(int)tag { if ((self = [super initWithFrame:frame])) { - /* Set the appropriate scale (for retina display support) */ - self.contentScaleFactor = scale; - self.tag = tag; - - [self updateDrawableSize]; + self.tag = METALVIEW_TAG; + /* Set the desired scale. The default drawableSize of a CAMetalLayer + * is its bounds x its scale so nothing further needs to be done. */ + self.layer.contentsScale = scale; } return self; @@ -63,16 +61,6 @@ - (void)layoutSubviews { [super layoutSubviews]; - [self updateDrawableSize]; -} - -- (void)updateDrawableSize -{ - CGSize size = self.bounds.size; - size.width *= self.contentScaleFactor; - size.height *= self.contentScaleFactor; - - ((CAMetalLayer *) self.layer).drawableSize = size; } @end @@ -89,9 +77,10 @@ UIKit_Mtl_AddMetalView(SDL_Window* window) } if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) { - /* Set the scale to the natural scale factor of the screen - the - * backing dimensions of the Metal view will match the pixel - * dimensions of the screen rather than the dimensions in points. + /* 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. */ #ifdef __IPHONE_8_0 if ([data.uiwindow.screen respondsToSelector:@selector(nativeScale)]) { @@ -104,8 +93,7 @@ UIKit_Mtl_AddMetalView(SDL_Window* window) } SDL_uikitmetalview *metalview = [[SDL_uikitmetalview alloc] initWithFrame:view.frame - scale:scale - tag:METALVIEW_TAG]; + scale:scale]; [metalview setSDLWindow:window]; return metalview;