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/22c8e7cd8d38 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
diff --git a/include/SDL_metal.h b/include/SDL_metal.h
index 3b7eb18..f967357 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 20d4ce2..c7411ca 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 28c800b..cf0dc12 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 b08fe74..9bbcc6c 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 38ed971..6f1faa0 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 76097a8..b6d083a 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 4dc5fa9..69c4fea 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 1c67c9f..605ecd3 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 f51c98c..76d0271 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 af41b9e..c856129 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 9ae37cb..cd63e78 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 29ed860..0882b39 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 4e01d17..ac53816 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 b92940b..18970b7 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