diff --git a/include/SDL_metal.h b/include/SDL_metal.h index 3b7eb18aa..f96735770 100644 --- a/include/SDL_metal.h +++ b/include/SDL_metal.h @@ -55,18 +55,13 @@ typedef void *SDL_MetalView; * On macOS, this does *not* associate a MTLDevice with the CAMetalLayer on its * own. It is up to user code to do that. * - * The returned handle can be casted directly to a NSView or UIView, and the - * CAMetalLayer can be accessed from the view's 'layer' property. + * The returned handle can be casted directly to a NSView or UIView. + * To access the backing CAMetalLayer, call SDL_Metal_GetLayer(). * - * \code - * SDL_MetalView metalview = SDL_Metal_CreateView(window); - * UIView *uiview = (__bridge UIView *)metalview; - * CAMetalLayer *metallayer = (CAMetalLayer *)uiview.layer; - * // [...] - * SDL_Metal_DestroyView(metalview); - * \endcode + * \note \a window must be created with the SDL_WINDOW_METAL flag. * * \sa SDL_Metal_DestroyView + * \sa SDL_Metal_GetLayer */ extern DECLSPEC SDL_MetalView SDLCALL SDL_Metal_CreateView(SDL_Window * window); @@ -80,6 +75,37 @@ extern DECLSPEC SDL_MetalView SDLCALL SDL_Metal_CreateView(SDL_Window * window); */ extern DECLSPEC void SDLCALL SDL_Metal_DestroyView(SDL_MetalView view); +/** + * \brief Get a pointer to the backing CAMetalLayer for the given view. + * + * \sa SDL_MetalCreateView + */ +extern DECLSPEC void *SDLCALL SDL_Metal_GetLayer(SDL_MetalView view); + +/** + * \brief Get the size of a window's underlying drawable in pixels (for use + * with setting viewport, scissor & etc). + * + * \param window SDL_Window from which the drawable size should be queried + * \param w Pointer to variable for storing the width in pixels, + * may be NULL + * \param h Pointer to variable for storing the height in pixels, + * may be NULL + * + * This may differ from SDL_GetWindowSize() if we're rendering to a high-DPI + * drawable, i.e. the window was created with SDL_WINDOW_ALLOW_HIGHDPI on a + * 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() + */ +extern DECLSPEC void SDLCALL SDL_Metal_GetDrawableSize(SDL_Window* window, int *w, + int *h); + /* @} *//* Metal support functions */ /* Ends C function definitions when using C++ */ diff --git a/include/SDL_video.h b/include/SDL_video.h index 20d4ce2d4..c7411ca49 100644 --- a/include/SDL_video.h +++ b/include/SDL_video.h @@ -118,7 +118,8 @@ typedef enum SDL_WINDOW_UTILITY = 0x00020000, /**< window should be treated as a utility window */ SDL_WINDOW_TOOLTIP = 0x00040000, /**< window should be treated as a tooltip */ SDL_WINDOW_POPUP_MENU = 0x00080000, /**< window should be treated as a popup menu */ - SDL_WINDOW_VULKAN = 0x10000000 /**< window usable for Vulkan surface */ + SDL_WINDOW_VULKAN = 0x10000000, /**< window usable for Vulkan surface */ + SDL_WINDOW_METAL = 0x20000000 /**< window usable for Metal view */ } SDL_WindowFlags; /** @@ -484,7 +485,8 @@ extern DECLSPEC Uint32 SDLCALL SDL_GetWindowPixelFormat(SDL_Window * window); * ::SDL_WINDOW_HIDDEN, ::SDL_WINDOW_BORDERLESS, * ::SDL_WINDOW_RESIZABLE, ::SDL_WINDOW_MAXIMIZED, * ::SDL_WINDOW_MINIMIZED, ::SDL_WINDOW_INPUT_GRABBED, - * ::SDL_WINDOW_ALLOW_HIGHDPI, ::SDL_WINDOW_VULKAN. + * ::SDL_WINDOW_ALLOW_HIGHDPI, ::SDL_WINDOW_VULKAN + * ::SDL_WINDOW_METAL. * * \return The created window, or NULL if window creation failed. * @@ -503,6 +505,9 @@ extern DECLSPEC Uint32 SDLCALL SDL_GetWindowPixelFormat(SDL_Window * window); * If SDL_WINDOW_VULKAN is specified and there isn't a working Vulkan driver, * SDL_CreateWindow() will fail because SDL_Vulkan_LoadLibrary() will fail. * + * If SDL_WINDOW_METAL is specified on an OS that does not support Metal, + * SDL_CreateWindow() will fail. + * * \note On non-Apple devices, SDL requires you to either not link to the * Vulkan loader or link to a dynamic library version. This limitation * may be removed in a future version of SDL. diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 28c800b96..cf0dc1287 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -759,3 +759,5 @@ #define SDL_GetErrorMsg SDL_GetErrorMsg_REAL #define SDL_LockSensors SDL_LockSensors_REAL #define SDL_UnlockSensors SDL_UnlockSensors_REAL +#define SDL_Metal_GetLayer SDL_Metal_GetLayer_REAL +#define SDL_Metal_GetDrawableSize SDL_Metal_GetDrawableSize_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index b08fe74b7..9bbcc6cce 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -818,3 +818,5 @@ SDL_DYNAPI_PROC(int,SDL_JoystickSetVirtualHat,(SDL_Joystick *a, int b, Uint8 c), SDL_DYNAPI_PROC(char*,SDL_GetErrorMsg,(char *a, int b),(a,b),return) SDL_DYNAPI_PROC(void,SDL_LockSensors,(void),(),) SDL_DYNAPI_PROC(void,SDL_UnlockSensors,(void),(),) +SDL_DYNAPI_PROC(void*,SDL_Metal_GetLayer,(SDL_MetalView a),(a),return) +SDL_DYNAPI_PROC(void,SDL_Metal_GetDrawableSize,(SDL_Window *a, int *b, int *c),(a,b,c),) diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 38ed97157..6f1faa031 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -281,6 +281,8 @@ struct SDL_VideoDevice */ SDL_MetalView (*Metal_CreateView) (_THIS, SDL_Window * window); void (*Metal_DestroyView) (_THIS, SDL_MetalView view); + void *(*Metal_GetLayer) (_THIS, SDL_MetalView view); + void (*Metal_GetDrawableSize) (_THIS, SDL_Window * window, int *w, int *h); /* * * */ /* diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 76097a8e4..b6d083a53 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -1366,7 +1366,7 @@ SDL_UpdateFullscreenMode(SDL_Window * window, SDL_bool fullscreen) } #define CREATE_FLAGS \ - (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_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) static SDL_INLINE SDL_bool IsAcceptingDragAndDrop(void) @@ -1455,7 +1455,7 @@ SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags) /* Some platforms have OpenGL enabled by default */ #if (SDL_VIDEO_OPENGL && __MACOSX__) || __IPHONEOS__ || __ANDROID__ || __NACL__ - if (!_this->is_dummy && !(flags & SDL_WINDOW_VULKAN) && !SDL_IsVideoContextExternal()) { + if (!_this->is_dummy && !(flags & SDL_WINDOW_VULKAN) && !(flags & SDL_WINDOW_METAL) && !SDL_IsVideoContextExternal()) { flags |= SDL_WINDOW_OPENGL; } #endif @@ -1487,6 +1487,24 @@ SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags) } } + if (flags & SDL_WINDOW_METAL) { + if (!_this->Metal_CreateView) { + SDL_SetError("Metal support is either not configured in SDL " + "or not available in current SDL video driver " + "(%s) or platform", _this->name); + return NULL; + } + if (flags & SDL_WINDOW_OPENGL) { + SDL_SetError("Metal and OpenGL not supported on same window"); + return NULL; + } + if (flags & SDL_WINDOW_VULKAN) { + SDL_SetError("Metal and Vulkan not supported on same window. " + "To use MoltenVK, set SDL_WINDOW_VULKAN only."); + return NULL; + } + } + /* Unless the user has specified the high-DPI disabling hint, respect the * SDL_WINDOW_ALLOW_HIGHDPI flag. */ @@ -1688,11 +1706,26 @@ SDL_RecreateWindow(SDL_Window * window, Uint32 flags) return -1; } + if ((window->flags & SDL_WINDOW_METAL) != (flags & SDL_WINDOW_METAL)) { + SDL_SetError("Can't change SDL_WINDOW_METAL window flag"); + return -1; + } + if ((window->flags & SDL_WINDOW_VULKAN) && (flags & SDL_WINDOW_OPENGL)) { SDL_SetError("Vulkan and OpenGL not supported on same window"); return -1; } + if ((window->flags & SDL_WINDOW_METAL) && (flags & SDL_WINDOW_OPENGL)) { + SDL_SetError("Metal and OpenGL not supported on same window"); + return -1; + } + + if ((window->flags & SDL_WINDOW_METAL) && (flags & SDL_WINDOW_VULKAN)) { + SDL_SetError("Metal and Vulkan not supported on same window"); + return -1; + } + window->flags = ((flags & CREATE_FLAGS) | SDL_WINDOW_HIDDEN); window->last_fullscreen_flags = window->flags; window->is_destroying = SDL_FALSE; @@ -4223,6 +4256,11 @@ 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; + } + if (_this->Metal_CreateView) { return _this->Metal_CreateView(_this, window); } else { @@ -4239,4 +4277,31 @@ SDL_Metal_DestroyView(SDL_MetalView view) } } +void * +SDL_Metal_GetLayer(SDL_MetalView view) +{ + if (_this && _this->Metal_GetLayer) { + if (view) { + return _this->Metal_GetLayer(_this, view); + } else { + SDL_InvalidParamError("view"); + return NULL; + } + } else { + SDL_SetError("Metal is not supported."); + return NULL; + } +} + +void SDL_Metal_GetDrawableSize(SDL_Window * window, int *w, int *h) +{ + CHECK_WINDOW_MAGIC(window,); + + if (_this->Metal_GetDrawableSize) { + _this->Metal_GetDrawableSize(_this, window, w, h); + } else { + SDL_GetWindowSize(window, w, h); + } +} + /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/cocoa/SDL_cocoametalview.h b/src/video/cocoa/SDL_cocoametalview.h index 4dc5fa9f4..69c4fea70 100644 --- a/src/video/cocoa/SDL_cocoametalview.h +++ b/src/video/cocoa/SDL_cocoametalview.h @@ -59,8 +59,8 @@ SDL_MetalView Cocoa_Metal_CreateView(_THIS, SDL_Window * window); void Cocoa_Metal_DestroyView(_THIS, SDL_MetalView view); - -void Cocoa_Metal_GetDrawableSize(SDL_Window * window, int * w, int * h); +void *Cocoa_Metal_GetLayer(_THIS, SDL_MetalView view); +void Cocoa_Metal_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h); #endif /* SDL_VIDEO_DRIVER_COCOA && (SDL_VIDEO_VULKAN || SDL_VIDEO_METAL) */ diff --git a/src/video/cocoa/SDL_cocoametalview.m b/src/video/cocoa/SDL_cocoametalview.m index 1c67c9f50..605ecd370 100644 --- a/src/video/cocoa/SDL_cocoametalview.m +++ b/src/video/cocoa/SDL_cocoametalview.m @@ -157,8 +157,15 @@ Cocoa_Metal_DestroyView(_THIS, SDL_MetalView view) [metalview removeFromSuperview]; }} +void * +Cocoa_Metal_GetLayer(_THIS, SDL_MetalView view) +{ @autoreleasepool { + SDL_cocoametalview *cocoaview = (__bridge SDL_cocoametalview *)view; + return (__bridge void *)cocoaview.layer; +}} + void -Cocoa_Metal_GetDrawableSize(SDL_Window * window, int * w, int * h) +Cocoa_Metal_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h) { @autoreleasepool { SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata; NSView *view = data->nswindow.contentView; diff --git a/src/video/cocoa/SDL_cocoavideo.m b/src/video/cocoa/SDL_cocoavideo.m index f51c98c42..76d02713c 100644 --- a/src/video/cocoa/SDL_cocoavideo.m +++ b/src/video/cocoa/SDL_cocoavideo.m @@ -146,6 +146,8 @@ Cocoa_CreateDevice(int devindex) #if SDL_VIDEO_METAL device->Metal_CreateView = Cocoa_Metal_CreateView; device->Metal_DestroyView = Cocoa_Metal_DestroyView; + device->Metal_GetLayer = Cocoa_Metal_GetLayer; + device->Metal_GetDrawableSize = Cocoa_Metal_GetDrawableSize; #endif device->StartTextInput = Cocoa_StartTextInput; diff --git a/src/video/cocoa/SDL_cocoavulkan.m b/src/video/cocoa/SDL_cocoavulkan.m index af41b9e19..c8561299d 100644 --- a/src/video/cocoa/SDL_cocoavulkan.m +++ b/src/video/cocoa/SDL_cocoavulkan.m @@ -236,7 +236,7 @@ SDL_bool Cocoa_Vulkan_CreateSurface(_THIS, void Cocoa_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h) { - Cocoa_Metal_GetDrawableSize(window, w, h); + Cocoa_Metal_GetDrawableSize(_this, window, w, h); } #endif diff --git a/src/video/uikit/SDL_uikitmetalview.h b/src/video/uikit/SDL_uikitmetalview.h index 9ae37cb67..cd63e7852 100644 --- a/src/video/uikit/SDL_uikitmetalview.h +++ b/src/video/uikit/SDL_uikitmetalview.h @@ -49,8 +49,8 @@ SDL_MetalView UIKit_Metal_CreateView(_THIS, SDL_Window * window); void UIKit_Metal_DestroyView(_THIS, SDL_MetalView view); - -void UIKit_Metal_GetDrawableSize(SDL_Window * window, int * w, int * h); +void *UIKit_Metal_GetLayer(_THIS, SDL_MetalView view); +void UIKit_Metal_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h); #endif /* SDL_VIDEO_DRIVER_UIKIT && (SDL_VIDEO_VULKAN || SDL_VIDEO_METAL) */ diff --git a/src/video/uikit/SDL_uikitmetalview.m b/src/video/uikit/SDL_uikitmetalview.m index 29ed8606d..0882b39db 100644 --- a/src/video/uikit/SDL_uikitmetalview.m +++ b/src/video/uikit/SDL_uikitmetalview.m @@ -110,8 +110,15 @@ UIKit_Metal_DestroyView(_THIS, SDL_MetalView view) } }} +void * +UIKit_Metal_GetLayer(_THIS, SDL_MetalView view) +{ @autoreleasepool { + SDL_uikitview *uiview = (__bridge SDL_uikitview *)view; + return (__bridge void *)uiview.layer; +}} + void -UIKit_Metal_GetDrawableSize(SDL_Window * window, int * w, int * h) +UIKit_Metal_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h) { @autoreleasepool { SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata; diff --git a/src/video/uikit/SDL_uikitvideo.m b/src/video/uikit/SDL_uikitvideo.m index 4e01d17df..ac538162d 100644 --- a/src/video/uikit/SDL_uikitvideo.m +++ b/src/video/uikit/SDL_uikitvideo.m @@ -140,6 +140,8 @@ UIKit_CreateDevice(int devindex) #if SDL_VIDEO_METAL device->Metal_CreateView = UIKit_Metal_CreateView; device->Metal_DestroyView = UIKit_Metal_DestroyView; + device->Metal_GetLayer = UIKit_Metal_GetLayer; + device->Metal_GetDrawableSize = UIKit_Metal_GetDrawableSize; #endif device->gl_config.accelerated = 1; diff --git a/src/video/uikit/SDL_uikitvulkan.m b/src/video/uikit/SDL_uikitvulkan.m index b92940b83..18970b783 100644 --- a/src/video/uikit/SDL_uikitvulkan.m +++ b/src/video/uikit/SDL_uikitvulkan.m @@ -243,7 +243,7 @@ SDL_bool UIKit_Vulkan_CreateSurface(_THIS, void UIKit_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h) { - UIKit_Metal_GetDrawableSize(window, w, h); + UIKit_Metal_GetDrawableSize(_this, window, w, h); } #endif