metal: Added some support interfaces to Apple's Metal API (thanks, Caleb!).
authorRyan C. Gordon <icculus@icculus.org>
Fri, 10 Apr 2020 00:37:35 -0400
changeset 13724aca267270368
parent 13723 30ce6dd90b8f
child 13725 67759565622a
metal: Added some support interfaces to Apple's Metal API (thanks, Caleb!).

Caleb Cornett's comments:

"A few weeks ago, Alex added a partial Metal API to SDL2:

https://hg.libsdl.org/SDL/rev/b87ba0fad17e

I noticed it was missing a few features that would help Metal become a
first-class citizen in SDL, so I went ahead and wrote them! Here are the new
APIs:

1. SDL_WINDOW_METAL flag for SDL_CreateWindow(). This allows the programmer
to specify that they intend to create a window for use with SDL_MetalView.
The flag is used to ensure correct usage of the API and to prevent
accidentally defaulting to OpenGL on iOS.

2. SDL_Metal_GetLayer(). This function takes a SDL_MetalView and returns a
pointer to the view's backing CAMetalLayer. This simplifies things
considerably, since in the current version of the SDL_Metal API the
programmer is required to bridge-cast a SDL_MetalView handle to an NSView or
UIView (depending on the platform) and then extract the layer from there.
SDL_Metal_GetLayer automatically handles all of that, making the operation
simple and cross-platform.

3. SDL_Metal_GetDrawableSize(). This function already exists in the current
SDL_Metal API (and is used behind-the-scenes for SDL_Vulkan_GetDrawableSize
on Apple platforms) but was not publicly exposed. My patch exposes this
function for public use. It works just like you'd expect.

Tested on macOS 10.14 and iOS 12.4."

Fixes Bugzilla #4796.
include/SDL_metal.h
include/SDL_video.h
src/dynapi/SDL_dynapi_overrides.h
src/dynapi/SDL_dynapi_procs.h
src/video/SDL_sysvideo.h
src/video/SDL_video.c
src/video/cocoa/SDL_cocoametalview.h
src/video/cocoa/SDL_cocoametalview.m
src/video/cocoa/SDL_cocoavideo.m
src/video/cocoa/SDL_cocoavulkan.m
src/video/uikit/SDL_uikitmetalview.h
src/video/uikit/SDL_uikitmetalview.m
src/video/uikit/SDL_uikitvideo.m
src/video/uikit/SDL_uikitvulkan.m
     1.1 --- a/include/SDL_metal.h	Thu Apr 09 15:57:12 2020 +0100
     1.2 +++ b/include/SDL_metal.h	Fri Apr 10 00:37:35 2020 -0400
     1.3 @@ -55,18 +55,13 @@
     1.4   *  On macOS, this does *not* associate a MTLDevice with the CAMetalLayer on its
     1.5   *  own. It is up to user code to do that.
     1.6   *
     1.7 - *  The returned handle can be casted directly to a NSView or UIView, and the
     1.8 - *  CAMetalLayer can be accessed from the view's 'layer' property.
     1.9 + *  The returned handle can be casted directly to a NSView or UIView.
    1.10 + *  To access the backing CAMetalLayer, call SDL_Metal_GetLayer().
    1.11   *
    1.12 - *  \code
    1.13 - *  SDL_MetalView metalview = SDL_Metal_CreateView(window);
    1.14 - *  UIView *uiview = (__bridge UIView *)metalview;
    1.15 - *  CAMetalLayer *metallayer = (CAMetalLayer *)uiview.layer;
    1.16 - *  // [...]
    1.17 - *  SDL_Metal_DestroyView(metalview);
    1.18 - *  \endcode
    1.19 + *  \note \a window must be created with the SDL_WINDOW_METAL flag.
    1.20   *
    1.21   *  \sa SDL_Metal_DestroyView
    1.22 + *  \sa SDL_Metal_GetLayer
    1.23   */
    1.24  extern DECLSPEC SDL_MetalView SDLCALL SDL_Metal_CreateView(SDL_Window * window);
    1.25  
    1.26 @@ -80,6 +75,37 @@
    1.27   */
    1.28  extern DECLSPEC void SDLCALL SDL_Metal_DestroyView(SDL_MetalView view);
    1.29  
    1.30 +/**
    1.31 + *  \brief Get a pointer to the backing CAMetalLayer for the given view.
    1.32 + *
    1.33 + *  \sa SDL_MetalCreateView
    1.34 + */
    1.35 +extern DECLSPEC void *SDLCALL SDL_Metal_GetLayer(SDL_MetalView view);
    1.36 +
    1.37 +/**
    1.38 + *  \brief Get the size of a window's underlying drawable in pixels (for use
    1.39 + *         with setting viewport, scissor & etc).
    1.40 + *
    1.41 + *  \param window   SDL_Window from which the drawable size should be queried
    1.42 + *  \param w        Pointer to variable for storing the width in pixels,
    1.43 + *                  may be NULL
    1.44 + *  \param h        Pointer to variable for storing the height in pixels,
    1.45 + *                  may be NULL
    1.46 + *
    1.47 + * This may differ from SDL_GetWindowSize() if we're rendering to a high-DPI
    1.48 + * drawable, i.e. the window was created with SDL_WINDOW_ALLOW_HIGHDPI on a
    1.49 + * platform with high-DPI support (Apple calls this "Retina"), and not disabled
    1.50 + * by the \c SDL_HINT_VIDEO_HIGHDPI_DISABLED hint.
    1.51 + *
    1.52 + *  \note On macOS high-DPI support must be enabled for an application by
    1.53 + *        setting NSHighResolutionCapable to true in its Info.plist.
    1.54 + *
    1.55 + *  \sa SDL_GetWindowSize()
    1.56 + *  \sa SDL_CreateWindow()
    1.57 + */
    1.58 +extern DECLSPEC void SDLCALL SDL_Metal_GetDrawableSize(SDL_Window* window, int *w,
    1.59 +                                                       int *h);
    1.60 +
    1.61  /* @} *//* Metal support functions */
    1.62  
    1.63  /* Ends C function definitions when using C++ */
     2.1 --- a/include/SDL_video.h	Thu Apr 09 15:57:12 2020 +0100
     2.2 +++ b/include/SDL_video.h	Fri Apr 10 00:37:35 2020 -0400
     2.3 @@ -118,7 +118,8 @@
     2.4      SDL_WINDOW_UTILITY       = 0x00020000,      /**< window should be treated as a utility window */
     2.5      SDL_WINDOW_TOOLTIP       = 0x00040000,      /**< window should be treated as a tooltip */
     2.6      SDL_WINDOW_POPUP_MENU    = 0x00080000,      /**< window should be treated as a popup menu */
     2.7 -    SDL_WINDOW_VULKAN        = 0x10000000       /**< window usable for Vulkan surface */
     2.8 +    SDL_WINDOW_VULKAN        = 0x10000000,      /**< window usable for Vulkan surface */
     2.9 +    SDL_WINDOW_METAL         = 0x20000000       /**< window usable for Metal view */
    2.10  } SDL_WindowFlags;
    2.11  
    2.12  /**
    2.13 @@ -484,7 +485,8 @@
    2.14   *               ::SDL_WINDOW_HIDDEN,        ::SDL_WINDOW_BORDERLESS,
    2.15   *               ::SDL_WINDOW_RESIZABLE,     ::SDL_WINDOW_MAXIMIZED,
    2.16   *               ::SDL_WINDOW_MINIMIZED,     ::SDL_WINDOW_INPUT_GRABBED,
    2.17 - *               ::SDL_WINDOW_ALLOW_HIGHDPI, ::SDL_WINDOW_VULKAN.
    2.18 + *               ::SDL_WINDOW_ALLOW_HIGHDPI, ::SDL_WINDOW_VULKAN
    2.19 + *               ::SDL_WINDOW_METAL.
    2.20   *
    2.21   *  \return The created window, or NULL if window creation failed.
    2.22   *
    2.23 @@ -503,6 +505,9 @@
    2.24   *  If SDL_WINDOW_VULKAN is specified and there isn't a working Vulkan driver,
    2.25   *  SDL_CreateWindow() will fail because SDL_Vulkan_LoadLibrary() will fail.
    2.26   *
    2.27 + *  If SDL_WINDOW_METAL is specified on an OS that does not support Metal,
    2.28 + *  SDL_CreateWindow() will fail.
    2.29 + *
    2.30   *  \note On non-Apple devices, SDL requires you to either not link to the
    2.31   *        Vulkan loader or link to a dynamic library version. This limitation
    2.32   *        may be removed in a future version of SDL.
     3.1 --- a/src/dynapi/SDL_dynapi_overrides.h	Thu Apr 09 15:57:12 2020 +0100
     3.2 +++ b/src/dynapi/SDL_dynapi_overrides.h	Fri Apr 10 00:37:35 2020 -0400
     3.3 @@ -759,3 +759,5 @@
     3.4  #define SDL_GetErrorMsg SDL_GetErrorMsg_REAL
     3.5  #define SDL_LockSensors SDL_LockSensors_REAL
     3.6  #define SDL_UnlockSensors SDL_UnlockSensors_REAL
     3.7 +#define SDL_Metal_GetLayer SDL_Metal_GetLayer_REAL
     3.8 +#define SDL_Metal_GetDrawableSize SDL_Metal_GetDrawableSize_REAL
     4.1 --- a/src/dynapi/SDL_dynapi_procs.h	Thu Apr 09 15:57:12 2020 +0100
     4.2 +++ b/src/dynapi/SDL_dynapi_procs.h	Fri Apr 10 00:37:35 2020 -0400
     4.3 @@ -818,3 +818,5 @@
     4.4  SDL_DYNAPI_PROC(char*,SDL_GetErrorMsg,(char *a, int b),(a,b),return)
     4.5  SDL_DYNAPI_PROC(void,SDL_LockSensors,(void),(),)
     4.6  SDL_DYNAPI_PROC(void,SDL_UnlockSensors,(void),(),)
     4.7 +SDL_DYNAPI_PROC(void*,SDL_Metal_GetLayer,(SDL_MetalView a),(a),return)
     4.8 +SDL_DYNAPI_PROC(void,SDL_Metal_GetDrawableSize,(SDL_Window *a, int *b, int *c),(a,b,c),)
     5.1 --- a/src/video/SDL_sysvideo.h	Thu Apr 09 15:57:12 2020 +0100
     5.2 +++ b/src/video/SDL_sysvideo.h	Fri Apr 10 00:37:35 2020 -0400
     5.3 @@ -281,6 +281,8 @@
     5.4       */
     5.5      SDL_MetalView (*Metal_CreateView) (_THIS, SDL_Window * window);
     5.6      void (*Metal_DestroyView) (_THIS, SDL_MetalView view);
     5.7 +    void *(*Metal_GetLayer) (_THIS, SDL_MetalView view);
     5.8 +    void (*Metal_GetDrawableSize) (_THIS, SDL_Window * window, int *w, int *h);
     5.9  
    5.10      /* * * */
    5.11      /*
     6.1 --- a/src/video/SDL_video.c	Thu Apr 09 15:57:12 2020 +0100
     6.2 +++ b/src/video/SDL_video.c	Fri Apr 10 00:37:35 2020 -0400
     6.3 @@ -1366,7 +1366,7 @@
     6.4  }
     6.5  
     6.6  #define CREATE_FLAGS \
     6.7 -    (SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_SKIP_TASKBAR | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_VULKAN | SDL_WINDOW_MINIMIZED)
     6.8 +    (SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_ALWAYS_ON_TOP | SDL_WINDOW_SKIP_TASKBAR | SDL_WINDOW_POPUP_MENU | SDL_WINDOW_UTILITY | SDL_WINDOW_TOOLTIP | SDL_WINDOW_VULKAN | SDL_WINDOW_MINIMIZED | SDL_WINDOW_METAL)
     6.9  
    6.10  static SDL_INLINE SDL_bool
    6.11  IsAcceptingDragAndDrop(void)
    6.12 @@ -1455,7 +1455,7 @@
    6.13  
    6.14      /* Some platforms have OpenGL enabled by default */
    6.15  #if (SDL_VIDEO_OPENGL && __MACOSX__) || __IPHONEOS__ || __ANDROID__ || __NACL__
    6.16 -    if (!_this->is_dummy && !(flags & SDL_WINDOW_VULKAN) && !SDL_IsVideoContextExternal()) {
    6.17 +    if (!_this->is_dummy && !(flags & SDL_WINDOW_VULKAN) && !(flags & SDL_WINDOW_METAL) && !SDL_IsVideoContextExternal()) {
    6.18          flags |= SDL_WINDOW_OPENGL;
    6.19      }
    6.20  #endif
    6.21 @@ -1487,6 +1487,24 @@
    6.22          }
    6.23      }
    6.24  
    6.25 +    if (flags & SDL_WINDOW_METAL) {
    6.26 +        if (!_this->Metal_CreateView) {
    6.27 +            SDL_SetError("Metal support is either not configured in SDL "
    6.28 +                         "or not available in current SDL video driver "
    6.29 +                         "(%s) or platform", _this->name);
    6.30 +            return NULL;
    6.31 +        }
    6.32 +        if (flags & SDL_WINDOW_OPENGL) {
    6.33 +            SDL_SetError("Metal and OpenGL not supported on same window");
    6.34 +            return NULL;
    6.35 +        }
    6.36 +        if (flags & SDL_WINDOW_VULKAN) {
    6.37 +            SDL_SetError("Metal and Vulkan not supported on same window. "
    6.38 +                         "To use MoltenVK, set SDL_WINDOW_VULKAN only.");
    6.39 +            return NULL;
    6.40 +        }
    6.41 +    }
    6.42 +
    6.43      /* Unless the user has specified the high-DPI disabling hint, respect the
    6.44       * SDL_WINDOW_ALLOW_HIGHDPI flag.
    6.45       */
    6.46 @@ -1688,11 +1706,26 @@
    6.47          return -1;
    6.48      }
    6.49  
    6.50 +    if ((window->flags & SDL_WINDOW_METAL) != (flags & SDL_WINDOW_METAL)) {
    6.51 +        SDL_SetError("Can't change SDL_WINDOW_METAL window flag");
    6.52 +        return -1;
    6.53 +    }
    6.54 +
    6.55      if ((window->flags & SDL_WINDOW_VULKAN) && (flags & SDL_WINDOW_OPENGL)) {
    6.56          SDL_SetError("Vulkan and OpenGL not supported on same window");
    6.57          return -1;
    6.58      }
    6.59  
    6.60 +    if ((window->flags & SDL_WINDOW_METAL) && (flags & SDL_WINDOW_OPENGL)) {
    6.61 +        SDL_SetError("Metal and OpenGL not supported on same window");
    6.62 +        return -1;
    6.63 +    }
    6.64 +
    6.65 +    if ((window->flags & SDL_WINDOW_METAL) && (flags & SDL_WINDOW_VULKAN)) {
    6.66 +        SDL_SetError("Metal and Vulkan not supported on same window");
    6.67 +        return -1;
    6.68 +    }
    6.69 +
    6.70      window->flags = ((flags & CREATE_FLAGS) | SDL_WINDOW_HIDDEN);
    6.71      window->last_fullscreen_flags = window->flags;
    6.72      window->is_destroying = SDL_FALSE;
    6.73 @@ -4223,6 +4256,11 @@
    6.74  {
    6.75      CHECK_WINDOW_MAGIC(window, NULL);
    6.76  
    6.77 +    if (!(window->flags & SDL_WINDOW_METAL)) {
    6.78 +        SDL_SetError("The specified window isn't a Metal window");
    6.79 +        return NULL;
    6.80 +    }
    6.81 +
    6.82      if (_this->Metal_CreateView) {
    6.83          return _this->Metal_CreateView(_this, window);
    6.84      } else {
    6.85 @@ -4239,4 +4277,31 @@
    6.86      }
    6.87  }
    6.88  
    6.89 +void *
    6.90 +SDL_Metal_GetLayer(SDL_MetalView view)
    6.91 +{
    6.92 +    if (_this && _this->Metal_GetLayer) {
    6.93 +        if (view) {
    6.94 +            return _this->Metal_GetLayer(_this, view);
    6.95 +        } else {
    6.96 +            SDL_InvalidParamError("view");
    6.97 +            return NULL;
    6.98 +        }
    6.99 +    } else {
   6.100 +        SDL_SetError("Metal is not supported.");
   6.101 +        return NULL;
   6.102 +    }
   6.103 +}
   6.104 +
   6.105 +void SDL_Metal_GetDrawableSize(SDL_Window * window, int *w, int *h)
   6.106 +{
   6.107 +    CHECK_WINDOW_MAGIC(window,);
   6.108 +
   6.109 +    if (_this->Metal_GetDrawableSize) {
   6.110 +        _this->Metal_GetDrawableSize(_this, window, w, h);
   6.111 +    } else {
   6.112 +        SDL_GetWindowSize(window, w, h);
   6.113 +    }
   6.114 +}
   6.115 +
   6.116  /* vi: set ts=4 sw=4 expandtab: */
     7.1 --- a/src/video/cocoa/SDL_cocoametalview.h	Thu Apr 09 15:57:12 2020 +0100
     7.2 +++ b/src/video/cocoa/SDL_cocoametalview.h	Fri Apr 10 00:37:35 2020 -0400
     7.3 @@ -59,8 +59,8 @@
     7.4  
     7.5  SDL_MetalView Cocoa_Metal_CreateView(_THIS, SDL_Window * window);
     7.6  void Cocoa_Metal_DestroyView(_THIS, SDL_MetalView view);
     7.7 -
     7.8 -void Cocoa_Metal_GetDrawableSize(SDL_Window * window, int * w, int * h);
     7.9 +void *Cocoa_Metal_GetLayer(_THIS, SDL_MetalView view);
    7.10 +void Cocoa_Metal_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h);
    7.11  
    7.12  #endif /* SDL_VIDEO_DRIVER_COCOA && (SDL_VIDEO_VULKAN || SDL_VIDEO_METAL) */
    7.13  
     8.1 --- a/src/video/cocoa/SDL_cocoametalview.m	Thu Apr 09 15:57:12 2020 +0100
     8.2 +++ b/src/video/cocoa/SDL_cocoametalview.m	Fri Apr 10 00:37:35 2020 -0400
     8.3 @@ -157,8 +157,15 @@
     8.4      [metalview removeFromSuperview];
     8.5  }}
     8.6  
     8.7 +void *
     8.8 +Cocoa_Metal_GetLayer(_THIS, SDL_MetalView view)
     8.9 +{ @autoreleasepool {
    8.10 +    SDL_cocoametalview *cocoaview = (__bridge SDL_cocoametalview *)view;
    8.11 +    return (__bridge void *)cocoaview.layer;
    8.12 +}}
    8.13 +
    8.14  void
    8.15 -Cocoa_Metal_GetDrawableSize(SDL_Window * window, int * w, int * h)
    8.16 +Cocoa_Metal_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h)
    8.17  { @autoreleasepool {
    8.18      SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata;
    8.19      NSView *view = data->nswindow.contentView;
     9.1 --- a/src/video/cocoa/SDL_cocoavideo.m	Thu Apr 09 15:57:12 2020 +0100
     9.2 +++ b/src/video/cocoa/SDL_cocoavideo.m	Fri Apr 10 00:37:35 2020 -0400
     9.3 @@ -146,6 +146,8 @@
     9.4  #if SDL_VIDEO_METAL
     9.5      device->Metal_CreateView = Cocoa_Metal_CreateView;
     9.6      device->Metal_DestroyView = Cocoa_Metal_DestroyView;
     9.7 +    device->Metal_GetLayer = Cocoa_Metal_GetLayer;
     9.8 +    device->Metal_GetDrawableSize = Cocoa_Metal_GetDrawableSize;
     9.9  #endif
    9.10  
    9.11      device->StartTextInput = Cocoa_StartTextInput;
    10.1 --- a/src/video/cocoa/SDL_cocoavulkan.m	Thu Apr 09 15:57:12 2020 +0100
    10.2 +++ b/src/video/cocoa/SDL_cocoavulkan.m	Fri Apr 10 00:37:35 2020 -0400
    10.3 @@ -236,7 +236,7 @@
    10.4  
    10.5  void Cocoa_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h)
    10.6  {
    10.7 -    Cocoa_Metal_GetDrawableSize(window, w, h);
    10.8 +    Cocoa_Metal_GetDrawableSize(_this, window, w, h);
    10.9  }
   10.10  
   10.11  #endif
    11.1 --- a/src/video/uikit/SDL_uikitmetalview.h	Thu Apr 09 15:57:12 2020 +0100
    11.2 +++ b/src/video/uikit/SDL_uikitmetalview.h	Fri Apr 10 00:37:35 2020 -0400
    11.3 @@ -49,8 +49,8 @@
    11.4  
    11.5  SDL_MetalView UIKit_Metal_CreateView(_THIS, SDL_Window * window);
    11.6  void UIKit_Metal_DestroyView(_THIS, SDL_MetalView view);
    11.7 -
    11.8 -void UIKit_Metal_GetDrawableSize(SDL_Window * window, int * w, int * h);
    11.9 +void *UIKit_Metal_GetLayer(_THIS, SDL_MetalView view);
   11.10 +void UIKit_Metal_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h);
   11.11  
   11.12  #endif /* SDL_VIDEO_DRIVER_UIKIT && (SDL_VIDEO_VULKAN || SDL_VIDEO_METAL) */
   11.13  
    12.1 --- a/src/video/uikit/SDL_uikitmetalview.m	Thu Apr 09 15:57:12 2020 +0100
    12.2 +++ b/src/video/uikit/SDL_uikitmetalview.m	Fri Apr 10 00:37:35 2020 -0400
    12.3 @@ -110,8 +110,15 @@
    12.4      }
    12.5  }}
    12.6  
    12.7 +void *
    12.8 +UIKit_Metal_GetLayer(_THIS, SDL_MetalView view)
    12.9 +{ @autoreleasepool {
   12.10 +    SDL_uikitview *uiview = (__bridge SDL_uikitview *)view;
   12.11 +    return (__bridge void *)uiview.layer;
   12.12 +}}
   12.13 +
   12.14  void
   12.15 -UIKit_Metal_GetDrawableSize(SDL_Window * window, int * w, int * h)
   12.16 +UIKit_Metal_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h)
   12.17  {
   12.18      @autoreleasepool {
   12.19          SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata;
    13.1 --- a/src/video/uikit/SDL_uikitvideo.m	Thu Apr 09 15:57:12 2020 +0100
    13.2 +++ b/src/video/uikit/SDL_uikitvideo.m	Fri Apr 10 00:37:35 2020 -0400
    13.3 @@ -140,6 +140,8 @@
    13.4  #if SDL_VIDEO_METAL
    13.5          device->Metal_CreateView = UIKit_Metal_CreateView;
    13.6          device->Metal_DestroyView = UIKit_Metal_DestroyView;
    13.7 +        device->Metal_GetLayer = UIKit_Metal_GetLayer;
    13.8 +        device->Metal_GetDrawableSize = UIKit_Metal_GetDrawableSize;
    13.9  #endif
   13.10  
   13.11          device->gl_config.accelerated = 1;
    14.1 --- a/src/video/uikit/SDL_uikitvulkan.m	Thu Apr 09 15:57:12 2020 +0100
    14.2 +++ b/src/video/uikit/SDL_uikitvulkan.m	Fri Apr 10 00:37:35 2020 -0400
    14.3 @@ -243,7 +243,7 @@
    14.4  
    14.5  void UIKit_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h)
    14.6  {
    14.7 -    UIKit_Metal_GetDrawableSize(window, w, h);
    14.8 +    UIKit_Metal_GetDrawableSize(_this, window, w, h);
    14.9  }
   14.10  
   14.11  #endif