src/render/metal/SDL_render_metal.m
author Ryan C. Gordon <icculus@icculus.org>
Thu, 06 Sep 2018 00:56:13 -0400
changeset 12179 c06be57c5a01
parent 12169 be34b836f738
child 12213 e4f5271ec8cc
child 12320 758d4e1222a7
permissions -rw-r--r--
metal: Make sure layer drawableSize is adjusted on resize.

Fixes Bugzilla #4250.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #if SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED
    24 
    25 #include "SDL_hints.h"
    26 #include "SDL_log.h"
    27 #include "SDL_assert.h"
    28 #include "SDL_syswm.h"
    29 #include "../SDL_sysrender.h"
    30 
    31 #ifdef __MACOSX__
    32 #include "../../video/cocoa/SDL_cocoametalview.h"
    33 #else
    34 #include "../../video/uikit/SDL_uikitmetalview.h"
    35 #endif
    36 #include <Availability.h>
    37 #import <Metal/Metal.h>
    38 #import <QuartzCore/CAMetalLayer.h>
    39 
    40 /* Regenerate these with build-metal-shaders.sh */
    41 #ifdef __MACOSX__
    42 #include "SDL_shaders_metal_osx.h"
    43 #else
    44 #include "SDL_shaders_metal_ios.h"
    45 #endif
    46 
    47 /* Apple Metal renderer implementation */
    48 
    49 static SDL_Renderer *METAL_CreateRenderer(SDL_Window * window, Uint32 flags);
    50 static void METAL_WindowEvent(SDL_Renderer * renderer,
    51                            const SDL_WindowEvent *event);
    52 static int METAL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h);
    53 static SDL_bool METAL_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode);
    54 static int METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    55 static int METAL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    56                             const SDL_Rect * rect, const void *pixels,
    57                             int pitch);
    58 static int METAL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
    59                                const SDL_Rect * rect,
    60                                const Uint8 *Yplane, int Ypitch,
    61                                const Uint8 *Uplane, int Upitch,
    62                                const Uint8 *Vplane, int Vpitch);
    63 static int METAL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    64                           const SDL_Rect * rect, void **pixels, int *pitch);
    65 static void METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    66 static int METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
    67 static int METAL_UpdateViewport(SDL_Renderer * renderer);
    68 static int METAL_UpdateClipRect(SDL_Renderer * renderer);
    69 static int METAL_RenderClear(SDL_Renderer * renderer);
    70 static int METAL_RenderDrawPoints(SDL_Renderer * renderer,
    71                                const SDL_FPoint * points, int count);
    72 static int METAL_RenderDrawLines(SDL_Renderer * renderer,
    73                               const SDL_FPoint * points, int count);
    74 static int METAL_RenderFillRects(SDL_Renderer * renderer,
    75                               const SDL_FRect * rects, int count);
    76 static int METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
    77                          const SDL_Rect * srcrect, const SDL_FRect * dstrect);
    78 static int METAL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
    79                          const SDL_Rect * srcrect, const SDL_FRect * dstrect,
    80                          const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
    81 static int METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
    82                                Uint32 pixel_format, void * pixels, int pitch);
    83 static void METAL_RenderPresent(SDL_Renderer * renderer);
    84 static void METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    85 static void METAL_DestroyRenderer(SDL_Renderer * renderer);
    86 static void *METAL_GetMetalLayer(SDL_Renderer * renderer);
    87 static void *METAL_GetMetalCommandEncoder(SDL_Renderer * renderer);
    88 
    89 SDL_RenderDriver METAL_RenderDriver = {
    90     METAL_CreateRenderer,
    91     {
    92         "metal",
    93         (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE),
    94         6,
    95         {
    96             SDL_PIXELFORMAT_ARGB8888,
    97             SDL_PIXELFORMAT_ABGR8888,
    98             SDL_PIXELFORMAT_YV12,
    99             SDL_PIXELFORMAT_IYUV,
   100             SDL_PIXELFORMAT_NV12,
   101             SDL_PIXELFORMAT_NV21
   102         },
   103     0, 0,
   104     }
   105 };
   106 
   107 /* macOS requires constants in a buffer to have a 256 byte alignment. */
   108 #ifdef __MACOSX__
   109 #define CONSTANT_ALIGN 256
   110 #else
   111 #define CONSTANT_ALIGN 4
   112 #endif
   113 
   114 #define ALIGN_CONSTANTS(size) ((size + CONSTANT_ALIGN - 1) & (~(CONSTANT_ALIGN - 1)))
   115 
   116 static const size_t CONSTANTS_OFFSET_IDENTITY = 0;
   117 static const size_t CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM = ALIGN_CONSTANTS(CONSTANTS_OFFSET_IDENTITY + sizeof(float) * 16);
   118 static const size_t CONSTANTS_OFFSET_DECODE_JPEG = ALIGN_CONSTANTS(CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM + sizeof(float) * 16);
   119 static const size_t CONSTANTS_OFFSET_DECODE_BT601 = ALIGN_CONSTANTS(CONSTANTS_OFFSET_DECODE_JPEG + sizeof(float) * 4 * 4);
   120 static const size_t CONSTANTS_OFFSET_DECODE_BT709 = ALIGN_CONSTANTS(CONSTANTS_OFFSET_DECODE_BT601 + sizeof(float) * 4 * 4);
   121 static const size_t CONSTANTS_OFFSET_CLEAR_VERTS = ALIGN_CONSTANTS(CONSTANTS_OFFSET_DECODE_BT709 + sizeof(float) * 4 * 4);
   122 static const size_t CONSTANTS_LENGTH = CONSTANTS_OFFSET_CLEAR_VERTS + sizeof(float) * 6;
   123 
   124 typedef enum SDL_MetalVertexFunction
   125 {
   126     SDL_METAL_VERTEX_SOLID,
   127     SDL_METAL_VERTEX_COPY,
   128 } SDL_MetalVertexFunction;
   129 
   130 typedef enum SDL_MetalFragmentFunction
   131 {
   132     SDL_METAL_FRAGMENT_SOLID = 0,
   133     SDL_METAL_FRAGMENT_COPY,
   134     SDL_METAL_FRAGMENT_YUV,
   135     SDL_METAL_FRAGMENT_NV12,
   136     SDL_METAL_FRAGMENT_NV21,
   137     SDL_METAL_FRAGMENT_COUNT,
   138 } SDL_MetalFragmentFunction;
   139 
   140 typedef struct METAL_PipelineState
   141 {
   142     SDL_BlendMode blendMode;
   143     void *pipe;
   144 } METAL_PipelineState;
   145 
   146 typedef struct METAL_PipelineCache
   147 {
   148     METAL_PipelineState *states;
   149     int count;
   150     SDL_MetalVertexFunction vertexFunction;
   151     SDL_MetalFragmentFunction fragmentFunction;
   152     MTLPixelFormat renderTargetFormat;
   153     const char *label;
   154 } METAL_PipelineCache;
   155 
   156 /* Each shader combination used by drawing functions has a separate pipeline
   157  * cache, and we have a separate list of caches for each render target pixel
   158  * format. This is more efficient than iterating over a global cache to find
   159  * the pipeline based on the specified shader combination and RT pixel format,
   160  * since we know what the RT pixel format is when we set the render target, and
   161  * we know what the shader combination is inside each drawing function's code. */
   162 typedef struct METAL_ShaderPipelines
   163 {
   164     MTLPixelFormat renderTargetFormat;
   165     METAL_PipelineCache caches[SDL_METAL_FRAGMENT_COUNT];
   166 } METAL_ShaderPipelines;
   167 
   168 @interface METAL_RenderData : NSObject
   169     @property (nonatomic, retain) id<MTLDevice> mtldevice;
   170     @property (nonatomic, retain) id<MTLCommandQueue> mtlcmdqueue;
   171     @property (nonatomic, retain) id<MTLCommandBuffer> mtlcmdbuffer;
   172     @property (nonatomic, retain) id<MTLRenderCommandEncoder> mtlcmdencoder;
   173     @property (nonatomic, retain) id<MTLLibrary> mtllibrary;
   174     @property (nonatomic, retain) id<CAMetalDrawable> mtlbackbuffer;
   175     @property (nonatomic, retain) id<MTLSamplerState> mtlsamplernearest;
   176     @property (nonatomic, retain) id<MTLSamplerState> mtlsamplerlinear;
   177     @property (nonatomic, retain) id<MTLBuffer> mtlbufconstants;
   178     @property (nonatomic, retain) CAMetalLayer *mtllayer;
   179     @property (nonatomic, retain) MTLRenderPassDescriptor *mtlpassdesc;
   180     @property (nonatomic, assign) METAL_ShaderPipelines *activepipelines;
   181     @property (nonatomic, assign) METAL_ShaderPipelines *allpipelines;
   182     @property (nonatomic, assign) int pipelinescount;
   183 @end
   184 
   185 @implementation METAL_RenderData
   186 #if !__has_feature(objc_arc)
   187 - (void)dealloc
   188 {
   189     [_mtldevice release];
   190     [_mtlcmdqueue release];
   191     [_mtlcmdbuffer release];
   192     [_mtlcmdencoder release];
   193     [_mtllibrary release];
   194     [_mtlbackbuffer release];
   195     [_mtlsamplernearest release];
   196     [_mtlsamplerlinear release];
   197     [_mtlbufconstants release];
   198     [_mtllayer release];
   199     [_mtlpassdesc release];
   200     [super dealloc];
   201 }
   202 #endif
   203 @end
   204 
   205 @interface METAL_TextureData : NSObject
   206     @property (nonatomic, retain) id<MTLTexture> mtltexture;
   207     @property (nonatomic, retain) id<MTLTexture> mtltexture_uv;
   208     @property (nonatomic, retain) id<MTLSamplerState> mtlsampler;
   209     @property (nonatomic, assign) SDL_MetalFragmentFunction fragmentFunction;
   210     @property (nonatomic, assign) BOOL yuv;
   211     @property (nonatomic, assign) BOOL nv12;
   212     @property (nonatomic, assign) size_t conversionBufferOffset;
   213 @end
   214 
   215 @implementation METAL_TextureData
   216 #if !__has_feature(objc_arc)
   217 - (void)dealloc
   218 {
   219     [_mtltexture release];
   220     [_mtltexture_uv release];
   221     [_mtlsampler release];
   222     [super dealloc];
   223 }
   224 #endif
   225 @end
   226 
   227 static int
   228 IsMetalAvailable(const SDL_SysWMinfo *syswm)
   229 {
   230     if (syswm->subsystem != SDL_SYSWM_COCOA && syswm->subsystem != SDL_SYSWM_UIKIT) {
   231         return SDL_SetError("Metal render target only supports Cocoa and UIKit video targets at the moment.");
   232     }
   233 
   234     // this checks a weak symbol.
   235 #if (defined(__MACOSX__) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101100))
   236     if (MTLCreateSystemDefaultDevice == NULL) {  // probably on 10.10 or lower.
   237         return SDL_SetError("Metal framework not available on this system");
   238     }
   239 #endif
   240 
   241     return 0;
   242 }
   243 
   244 static const MTLBlendOperation invalidBlendOperation = (MTLBlendOperation)0xFFFFFFFF;
   245 static const MTLBlendFactor invalidBlendFactor = (MTLBlendFactor)0xFFFFFFFF;
   246 
   247 static MTLBlendOperation
   248 GetBlendOperation(SDL_BlendOperation operation)
   249 {
   250     switch (operation) {
   251         case SDL_BLENDOPERATION_ADD: return MTLBlendOperationAdd;
   252         case SDL_BLENDOPERATION_SUBTRACT: return MTLBlendOperationSubtract;
   253         case SDL_BLENDOPERATION_REV_SUBTRACT: return MTLBlendOperationReverseSubtract;
   254         case SDL_BLENDOPERATION_MINIMUM: return MTLBlendOperationMin;
   255         case SDL_BLENDOPERATION_MAXIMUM: return MTLBlendOperationMax;
   256         default: return invalidBlendOperation;
   257     }
   258 }
   259 
   260 static MTLBlendFactor
   261 GetBlendFactor(SDL_BlendFactor factor)
   262 {
   263     switch (factor) {
   264         case SDL_BLENDFACTOR_ZERO: return MTLBlendFactorZero;
   265         case SDL_BLENDFACTOR_ONE: return MTLBlendFactorOne;
   266         case SDL_BLENDFACTOR_SRC_COLOR: return MTLBlendFactorSourceColor;
   267         case SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return MTLBlendFactorOneMinusSourceColor;
   268         case SDL_BLENDFACTOR_SRC_ALPHA: return MTLBlendFactorSourceAlpha;
   269         case SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return MTLBlendFactorOneMinusSourceAlpha;
   270         case SDL_BLENDFACTOR_DST_COLOR: return MTLBlendFactorDestinationColor;
   271         case SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR: return MTLBlendFactorOneMinusDestinationColor;
   272         case SDL_BLENDFACTOR_DST_ALPHA: return MTLBlendFactorDestinationAlpha;
   273         case SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return MTLBlendFactorOneMinusDestinationAlpha;
   274         default: return invalidBlendFactor;
   275     }
   276 }
   277 
   278 static NSString *
   279 GetVertexFunctionName(SDL_MetalVertexFunction function)
   280 {
   281     switch (function) {
   282         case SDL_METAL_VERTEX_SOLID: return @"SDL_Solid_vertex";
   283         case SDL_METAL_VERTEX_COPY: return @"SDL_Copy_vertex";
   284         default: return nil;
   285     }
   286 }
   287 
   288 static NSString *
   289 GetFragmentFunctionName(SDL_MetalFragmentFunction function)
   290 {
   291     switch (function) {
   292         case SDL_METAL_FRAGMENT_SOLID: return @"SDL_Solid_fragment";
   293         case SDL_METAL_FRAGMENT_COPY: return @"SDL_Copy_fragment";
   294         case SDL_METAL_FRAGMENT_YUV: return @"SDL_YUV_fragment";
   295         case SDL_METAL_FRAGMENT_NV12: return @"SDL_NV12_fragment";
   296         case SDL_METAL_FRAGMENT_NV21: return @"SDL_NV21_fragment";
   297         default: return nil;
   298     }
   299 }
   300 
   301 static id<MTLRenderPipelineState>
   302 MakePipelineState(METAL_RenderData *data, METAL_PipelineCache *cache,
   303                   NSString *blendlabel, SDL_BlendMode blendmode)
   304 {
   305     id<MTLFunction> mtlvertfn = [data.mtllibrary newFunctionWithName:GetVertexFunctionName(cache->vertexFunction)];
   306     id<MTLFunction> mtlfragfn = [data.mtllibrary newFunctionWithName:GetFragmentFunctionName(cache->fragmentFunction)];
   307     SDL_assert(mtlvertfn != nil);
   308     SDL_assert(mtlfragfn != nil);
   309 
   310     MTLRenderPipelineDescriptor *mtlpipedesc = [[MTLRenderPipelineDescriptor alloc] init];
   311     mtlpipedesc.vertexFunction = mtlvertfn;
   312     mtlpipedesc.fragmentFunction = mtlfragfn;
   313 
   314     MTLRenderPipelineColorAttachmentDescriptor *rtdesc = mtlpipedesc.colorAttachments[0];
   315 
   316     rtdesc.pixelFormat = cache->renderTargetFormat;
   317 
   318     if (blendmode != SDL_BLENDMODE_NONE) {
   319         rtdesc.blendingEnabled = YES;
   320         rtdesc.sourceRGBBlendFactor = GetBlendFactor(SDL_GetBlendModeSrcColorFactor(blendmode));
   321         rtdesc.destinationRGBBlendFactor = GetBlendFactor(SDL_GetBlendModeDstColorFactor(blendmode));
   322         rtdesc.rgbBlendOperation = GetBlendOperation(SDL_GetBlendModeColorOperation(blendmode));
   323         rtdesc.sourceAlphaBlendFactor = GetBlendFactor(SDL_GetBlendModeSrcAlphaFactor(blendmode));
   324         rtdesc.destinationAlphaBlendFactor = GetBlendFactor(SDL_GetBlendModeDstAlphaFactor(blendmode));
   325         rtdesc.alphaBlendOperation = GetBlendOperation(SDL_GetBlendModeAlphaOperation(blendmode));
   326     } else {
   327         rtdesc.blendingEnabled = NO;
   328     }
   329 
   330     mtlpipedesc.label = [@(cache->label) stringByAppendingString:blendlabel];
   331 
   332     NSError *err = nil;
   333     id<MTLRenderPipelineState> state = [data.mtldevice newRenderPipelineStateWithDescriptor:mtlpipedesc error:&err];
   334     SDL_assert(err == nil);
   335 
   336     METAL_PipelineState pipeline;
   337     pipeline.blendMode = blendmode;
   338     pipeline.pipe = (void *)CFBridgingRetain(state);
   339 
   340     METAL_PipelineState *states = SDL_realloc(cache->states, (cache->count + 1) * sizeof(pipeline));
   341 
   342 #if !__has_feature(objc_arc)
   343     [mtlpipedesc release];  // !!! FIXME: can these be reused for each creation, or does the pipeline obtain it?
   344     [mtlvertfn release];
   345     [mtlfragfn release];
   346     [state release];
   347 #endif
   348 
   349     if (states) {
   350         states[cache->count++] = pipeline;
   351         cache->states = states;
   352         return (__bridge id<MTLRenderPipelineState>)pipeline.pipe;
   353     } else {
   354         CFBridgingRelease(pipeline.pipe);
   355         SDL_OutOfMemory();
   356         return NULL;
   357     }
   358 }
   359 
   360 static void
   361 MakePipelineCache(METAL_RenderData *data, METAL_PipelineCache *cache, const char *label,
   362                   MTLPixelFormat rtformat, SDL_MetalVertexFunction vertfn, SDL_MetalFragmentFunction fragfn)
   363 {
   364     SDL_zerop(cache);
   365 
   366     cache->vertexFunction = vertfn;
   367     cache->fragmentFunction = fragfn;
   368     cache->renderTargetFormat = rtformat;
   369     cache->label = label;
   370 
   371     /* Create pipeline states for the default blend modes. Custom blend modes
   372      * will be added to the cache on-demand. */
   373     MakePipelineState(data, cache, @" (blend=none)", SDL_BLENDMODE_NONE);
   374     MakePipelineState(data, cache, @" (blend=blend)", SDL_BLENDMODE_BLEND);
   375     MakePipelineState(data, cache, @" (blend=add)", SDL_BLENDMODE_ADD);
   376     MakePipelineState(data, cache, @" (blend=mod)", SDL_BLENDMODE_MOD);
   377 }
   378 
   379 static void
   380 DestroyPipelineCache(METAL_PipelineCache *cache)
   381 {
   382     if (cache != NULL) {
   383         for (int i = 0; i < cache->count; i++) {
   384             CFBridgingRelease(cache->states[i].pipe);
   385         }
   386 
   387         SDL_free(cache->states);
   388     }
   389 }
   390 
   391 void
   392 MakeShaderPipelines(METAL_RenderData *data, METAL_ShaderPipelines *pipelines, MTLPixelFormat rtformat)
   393 {
   394     SDL_zerop(pipelines);
   395 
   396     pipelines->renderTargetFormat = rtformat;
   397 
   398     MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_SOLID], "SDL primitives pipeline", rtformat, SDL_METAL_VERTEX_SOLID, SDL_METAL_FRAGMENT_SOLID);
   399     MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_COPY], "SDL copy pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_COPY);
   400     MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_YUV], "SDL YUV pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_YUV);
   401     MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_NV12], "SDL NV12 pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_NV12);
   402     MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_NV21], "SDL NV21 pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_NV21);
   403 }
   404 
   405 static METAL_ShaderPipelines *
   406 ChooseShaderPipelines(METAL_RenderData *data, MTLPixelFormat rtformat)
   407 {
   408     METAL_ShaderPipelines *allpipelines = data.allpipelines;
   409     int count = data.pipelinescount;
   410 
   411     for (int i = 0; i < count; i++) {
   412         if (allpipelines[i].renderTargetFormat == rtformat) {
   413             return &allpipelines[i];
   414         }
   415     }
   416 
   417     allpipelines = SDL_realloc(allpipelines, (count + 1) * sizeof(METAL_ShaderPipelines));
   418 
   419     if (allpipelines == NULL) {
   420         SDL_OutOfMemory();
   421         return NULL;
   422     }
   423 
   424     MakeShaderPipelines(data, &allpipelines[count], rtformat);
   425 
   426     data.allpipelines = allpipelines;
   427     data.pipelinescount = count + 1;
   428 
   429     return &data.allpipelines[count];
   430 }
   431 
   432 static void
   433 DestroyAllPipelines(METAL_ShaderPipelines *allpipelines, int count)
   434 {
   435     if (allpipelines != NULL) {
   436         for (int i = 0; i < count; i++) {
   437             for (int cache = 0; cache < SDL_METAL_FRAGMENT_COUNT; cache++) {
   438                 DestroyPipelineCache(&allpipelines[i].caches[cache]);
   439             }
   440         }
   441 
   442         SDL_free(allpipelines);
   443     }
   444 }
   445 
   446 static inline id<MTLRenderPipelineState>
   447 ChoosePipelineState(METAL_RenderData *data, METAL_ShaderPipelines *pipelines, SDL_MetalFragmentFunction fragfn, SDL_BlendMode blendmode)
   448 {
   449     METAL_PipelineCache *cache = &pipelines->caches[fragfn];
   450 
   451     for (int i = 0; i < cache->count; i++) {
   452         if (cache->states[i].blendMode == blendmode) {
   453             return (__bridge id<MTLRenderPipelineState>)cache->states[i].pipe;
   454         }
   455     }
   456 
   457     return MakePipelineState(data, cache, [NSString stringWithFormat:@" (blend=custom 0x%x)", blendmode], blendmode);
   458 }
   459 
   460 static SDL_Renderer *
   461 METAL_CreateRenderer(SDL_Window * window, Uint32 flags)
   462 { @autoreleasepool {
   463     SDL_Renderer *renderer = NULL;
   464     METAL_RenderData *data = NULL;
   465     id<MTLDevice> mtldevice = nil;
   466     SDL_SysWMinfo syswm;
   467 
   468     SDL_VERSION(&syswm.version);
   469     if (!SDL_GetWindowWMInfo(window, &syswm)) {
   470         return NULL;
   471     }
   472 
   473     if (IsMetalAvailable(&syswm) == -1) {
   474         return NULL;
   475     }
   476 
   477     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
   478     if (!renderer) {
   479         SDL_OutOfMemory();
   480         return NULL;
   481     }
   482 
   483     // !!! FIXME: MTLCopyAllDevices() can find other GPUs on macOS...
   484     mtldevice = MTLCreateSystemDefaultDevice();
   485 
   486     if (mtldevice == nil) {
   487         SDL_free(renderer);
   488         SDL_SetError("Failed to obtain Metal device");
   489         return NULL;
   490     }
   491 
   492     // !!! FIXME: error checking on all of this.
   493     data = [[METAL_RenderData alloc] init];
   494 
   495     renderer->driverdata = (void*)CFBridgingRetain(data);
   496     renderer->window = window;
   497 
   498 #ifdef __MACOSX__
   499     NSView *view = Cocoa_Mtl_AddMetalView(window);
   500     CAMetalLayer *layer = (CAMetalLayer *)[view layer];
   501 
   502     layer.device = mtldevice;
   503 
   504     //layer.colorspace = nil;
   505 
   506 #else
   507     UIView *view = UIKit_Mtl_AddMetalView(window);
   508     CAMetalLayer *layer = (CAMetalLayer *)[view layer];
   509 #endif
   510 
   511     // Necessary for RenderReadPixels.
   512     layer.framebufferOnly = NO;
   513 
   514     data.mtldevice = layer.device;
   515     data.mtllayer = layer;
   516     id<MTLCommandQueue> mtlcmdqueue = [data.mtldevice newCommandQueue];
   517     data.mtlcmdqueue = mtlcmdqueue;
   518     data.mtlcmdqueue.label = @"SDL Metal Renderer";
   519     data.mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor];
   520 
   521     NSError *err = nil;
   522 
   523     // The compiled .metallib is embedded in a static array in a header file
   524     // but the original shader source code is in SDL_shaders_metal.metal.
   525     dispatch_data_t mtllibdata = dispatch_data_create(sdl_metallib, sdl_metallib_len, dispatch_get_global_queue(0, 0), ^{});
   526     id<MTLLibrary> mtllibrary = [data.mtldevice newLibraryWithData:mtllibdata error:&err];
   527     data.mtllibrary = mtllibrary;
   528     SDL_assert(err == nil);
   529 #if !__has_feature(objc_arc)
   530     dispatch_release(mtllibdata);
   531 #endif
   532     data.mtllibrary.label = @"SDL Metal renderer shader library";
   533 
   534     /* Do some shader pipeline state loading up-front rather than on demand. */
   535     data.pipelinescount = 0;
   536     data.allpipelines = NULL;
   537     ChooseShaderPipelines(data, MTLPixelFormatBGRA8Unorm);
   538 
   539     MTLSamplerDescriptor *samplerdesc = [[MTLSamplerDescriptor alloc] init];
   540 
   541     samplerdesc.minFilter = MTLSamplerMinMagFilterNearest;
   542     samplerdesc.magFilter = MTLSamplerMinMagFilterNearest;
   543     id<MTLSamplerState> mtlsamplernearest = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc];
   544     data.mtlsamplernearest = mtlsamplernearest;
   545 
   546     samplerdesc.minFilter = MTLSamplerMinMagFilterLinear;
   547     samplerdesc.magFilter = MTLSamplerMinMagFilterLinear;
   548     id<MTLSamplerState> mtlsamplerlinear = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc];
   549     data.mtlsamplerlinear = mtlsamplerlinear;
   550 
   551     /* Note: matrices are column major. */
   552     float identitytransform[16] = {
   553         1.0f, 0.0f, 0.0f, 0.0f,
   554         0.0f, 1.0f, 0.0f, 0.0f,
   555         0.0f, 0.0f, 1.0f, 0.0f,
   556         0.0f, 0.0f, 0.0f, 1.0f,
   557     };
   558 
   559     float halfpixeltransform[16] = {
   560         1.0f, 0.0f, 0.0f, 0.0f,
   561         0.0f, 1.0f, 0.0f, 0.0f,
   562         0.0f, 0.0f, 1.0f, 0.0f,
   563         0.5f, 0.5f, 0.0f, 1.0f,
   564     };
   565 
   566     /* Metal pads float3s to 16 bytes. */
   567     float decodetransformJPEG[4*4] = {
   568         0.0, -0.501960814, -0.501960814, 0.0, /* offset */
   569         1.0000,  0.0000,  1.4020, 0.0,        /* Rcoeff */
   570         1.0000, -0.3441, -0.7141, 0.0,        /* Gcoeff */
   571         1.0000,  1.7720,  0.0000, 0.0,        /* Bcoeff */
   572     };
   573 
   574     float decodetransformBT601[4*4] = {
   575         -0.0627451017, -0.501960814, -0.501960814, 0.0, /* offset */
   576         1.1644,  0.0000,  1.5960, 0.0,                  /* Rcoeff */
   577         1.1644, -0.3918, -0.8130, 0.0,                  /* Gcoeff */
   578         1.1644,  2.0172,  0.0000, 0.0,                  /* Bcoeff */
   579     };
   580 
   581     float decodetransformBT709[4*4] = {
   582         0.0, -0.501960814, -0.501960814, 0.0, /* offset */
   583         1.0000,  0.0000,  1.4020, 0.0,        /* Rcoeff */
   584         1.0000, -0.3441, -0.7141, 0.0,        /* Gcoeff */
   585         1.0000,  1.7720,  0.0000, 0.0,        /* Bcoeff */
   586     };
   587 
   588     float clearverts[6] = {0.0f, 0.0f,  0.0f, 2.0f,  2.0f, 0.0f};
   589 
   590     id<MTLBuffer> mtlbufconstantstaging = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModeShared];
   591     mtlbufconstantstaging.label = @"SDL constant staging data";
   592 
   593     id<MTLBuffer> mtlbufconstants = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModePrivate];
   594     data.mtlbufconstants = mtlbufconstants;
   595     data.mtlbufconstants.label = @"SDL constant data";
   596 
   597     char *constantdata = [mtlbufconstantstaging contents];
   598     SDL_memcpy(constantdata + CONSTANTS_OFFSET_IDENTITY, identitytransform, sizeof(identitytransform));
   599     SDL_memcpy(constantdata + CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM, halfpixeltransform, sizeof(halfpixeltransform));
   600     SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_JPEG, decodetransformJPEG, sizeof(decodetransformJPEG));
   601     SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT601, decodetransformBT601, sizeof(decodetransformBT601));
   602     SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT709, decodetransformBT709, sizeof(decodetransformBT709));
   603     SDL_memcpy(constantdata + CONSTANTS_OFFSET_CLEAR_VERTS, clearverts, sizeof(clearverts));
   604 
   605     id<MTLCommandBuffer> cmdbuffer = [data.mtlcmdqueue commandBuffer];
   606     id<MTLBlitCommandEncoder> blitcmd = [cmdbuffer blitCommandEncoder];
   607 
   608     [blitcmd copyFromBuffer:mtlbufconstantstaging sourceOffset:0 toBuffer:data.mtlbufconstants destinationOffset:0 size:CONSTANTS_LENGTH];
   609 
   610     [blitcmd endEncoding];
   611     [cmdbuffer commit];
   612 
   613     // !!! FIXME: force more clears here so all the drawables are sane to start, and our static buffers are definitely flushed.
   614 
   615     renderer->WindowEvent = METAL_WindowEvent;
   616     renderer->GetOutputSize = METAL_GetOutputSize;
   617     renderer->SupportsBlendMode = METAL_SupportsBlendMode;
   618     renderer->CreateTexture = METAL_CreateTexture;
   619     renderer->UpdateTexture = METAL_UpdateTexture;
   620     renderer->UpdateTextureYUV = METAL_UpdateTextureYUV;
   621     renderer->LockTexture = METAL_LockTexture;
   622     renderer->UnlockTexture = METAL_UnlockTexture;
   623     renderer->SetRenderTarget = METAL_SetRenderTarget;
   624     renderer->UpdateViewport = METAL_UpdateViewport;
   625     renderer->UpdateClipRect = METAL_UpdateClipRect;
   626     renderer->RenderClear = METAL_RenderClear;
   627     renderer->RenderDrawPoints = METAL_RenderDrawPoints;
   628     renderer->RenderDrawLines = METAL_RenderDrawLines;
   629     renderer->RenderFillRects = METAL_RenderFillRects;
   630     renderer->RenderCopy = METAL_RenderCopy;
   631     renderer->RenderCopyEx = METAL_RenderCopyEx;
   632     renderer->RenderReadPixels = METAL_RenderReadPixels;
   633     renderer->RenderPresent = METAL_RenderPresent;
   634     renderer->DestroyTexture = METAL_DestroyTexture;
   635     renderer->DestroyRenderer = METAL_DestroyRenderer;
   636     renderer->GetMetalLayer = METAL_GetMetalLayer;
   637     renderer->GetMetalCommandEncoder = METAL_GetMetalCommandEncoder;
   638 
   639     renderer->info = METAL_RenderDriver.info;
   640     renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
   641 
   642 #if defined(__MACOSX__) && defined(MAC_OS_X_VERSION_10_13)
   643     if (@available(macOS 10.13, *)) {
   644         data.mtllayer.displaySyncEnabled = (flags & SDL_RENDERER_PRESENTVSYNC) != 0;
   645     } else
   646 #endif
   647     {
   648         renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
   649     }
   650 
   651     /* https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */
   652     int maxtexsize = 4096;
   653 #if defined(__MACOSX__)
   654     maxtexsize = 16384;
   655 #elif defined(__TVOS__)
   656     maxtexsize = 8192;
   657 #ifdef __TVOS_11_0
   658     if (@available(tvOS 11.0, *)) {
   659         if ([mtldevice supportsFeatureSet:MTLFeatureSet_tvOS_GPUFamily2_v1]) {
   660             maxtexsize = 16384;
   661         }
   662     }
   663 #endif
   664 #else
   665 #ifdef __IPHONE_11_0
   666     if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily4_v1]) {
   667         maxtexsize = 16384;
   668     } else
   669 #endif
   670 #ifdef __IPHONE_10_0
   671     if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
   672         maxtexsize = 16384;
   673     } else
   674 #endif
   675     if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v2] || [mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v2]) {
   676         maxtexsize = 8192;
   677     } else {
   678         maxtexsize = 4096;
   679     }
   680 #endif
   681 
   682     renderer->info.max_texture_width = maxtexsize;
   683     renderer->info.max_texture_height = maxtexsize;
   684 
   685 #if !__has_feature(objc_arc)
   686     [mtlcmdqueue release];
   687     [mtllibrary release];
   688     [samplerdesc release];
   689     [mtlsamplernearest release];
   690     [mtlsamplerlinear release];
   691     [mtlbufconstants release];
   692     [view release];
   693     [data release];
   694     [mtldevice release];
   695 #endif
   696 
   697     return renderer;
   698 }}
   699 
   700 static void
   701 METAL_ActivateRenderCommandEncoder(SDL_Renderer * renderer, MTLLoadAction load)
   702 {
   703     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
   704 
   705     /* Our SetRenderTarget just signals that the next render operation should
   706      * set up a new render pass. This is where that work happens. */
   707     if (data.mtlcmdencoder == nil) {
   708         id<MTLTexture> mtltexture = nil;
   709 
   710         if (renderer->target != NULL) {
   711             METAL_TextureData *texdata = (__bridge METAL_TextureData *)renderer->target->driverdata;
   712             mtltexture = texdata.mtltexture;
   713         } else {
   714             if (data.mtlbackbuffer == nil) {
   715                 /* The backbuffer's contents aren't guaranteed to persist after
   716                  * presenting, so we can leave it undefined when loading it. */
   717                 data.mtlbackbuffer = [data.mtllayer nextDrawable];
   718                 if (load == MTLLoadActionLoad) {
   719                     load = MTLLoadActionDontCare;
   720                 }
   721             }
   722             mtltexture = data.mtlbackbuffer.texture;
   723         }
   724 
   725         SDL_assert(mtltexture);
   726 
   727         if (load == MTLLoadActionClear) {
   728             MTLClearColor color = MTLClearColorMake(renderer->r/255.0, renderer->g/255.0, renderer->b/255.0, renderer->a/255.0);
   729             data.mtlpassdesc.colorAttachments[0].clearColor = color;
   730         }
   731 
   732         data.mtlpassdesc.colorAttachments[0].loadAction = load;
   733         data.mtlpassdesc.colorAttachments[0].texture = mtltexture;
   734 
   735         data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
   736         data.mtlcmdencoder = [data.mtlcmdbuffer renderCommandEncoderWithDescriptor:data.mtlpassdesc];
   737 
   738         if (data.mtlbackbuffer != nil && mtltexture == data.mtlbackbuffer.texture) {
   739             data.mtlcmdencoder.label = @"SDL metal renderer backbuffer";
   740         } else {
   741             data.mtlcmdencoder.label = @"SDL metal renderer render target";
   742         }
   743 
   744         data.activepipelines = ChooseShaderPipelines(data, mtltexture.pixelFormat);
   745 
   746         /* Make sure the viewport and clip rect are set on the new render pass. */
   747         METAL_UpdateViewport(renderer);
   748         METAL_UpdateClipRect(renderer);
   749     }
   750 }
   751 
   752 static void
   753 METAL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
   754 {
   755     if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) {
   756         METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
   757         data.mtllayer.drawableSize = CGSizeMake(event->data1, event->data2);
   758     }
   759 
   760     if (event->event == SDL_WINDOWEVENT_SHOWN ||
   761         event->event == SDL_WINDOWEVENT_HIDDEN) {
   762         // !!! FIXME: write me
   763     }
   764 }
   765 
   766 static int
   767 METAL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
   768 { @autoreleasepool {
   769     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
   770     if (w) {
   771         *w = (int)data.mtllayer.drawableSize.width;
   772     }
   773     if (h) {
   774         *h = (int)data.mtllayer.drawableSize.height;
   775     }
   776     return 0;
   777 }}
   778 
   779 static SDL_bool
   780 METAL_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
   781 {
   782     SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode);
   783     SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode);
   784     SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode);
   785     SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode);
   786     SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode);
   787     SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode);
   788 
   789     if (GetBlendFactor(srcColorFactor) == invalidBlendFactor ||
   790         GetBlendFactor(srcAlphaFactor) == invalidBlendFactor ||
   791         GetBlendOperation(colorOperation) == invalidBlendOperation ||
   792         GetBlendFactor(dstColorFactor) == invalidBlendFactor ||
   793         GetBlendFactor(dstAlphaFactor) == invalidBlendFactor ||
   794         GetBlendOperation(alphaOperation) == invalidBlendOperation) {
   795         return SDL_FALSE;
   796     }
   797     return SDL_TRUE;
   798 }
   799 
   800 static int
   801 METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   802 { @autoreleasepool {
   803     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
   804     MTLPixelFormat pixfmt;
   805 
   806     switch (texture->format) {
   807         case SDL_PIXELFORMAT_ABGR8888:
   808             pixfmt = MTLPixelFormatRGBA8Unorm;
   809             break;
   810         case SDL_PIXELFORMAT_ARGB8888:
   811             pixfmt = MTLPixelFormatBGRA8Unorm;
   812             break;
   813         case SDL_PIXELFORMAT_IYUV:
   814         case SDL_PIXELFORMAT_YV12:
   815         case SDL_PIXELFORMAT_NV12:
   816         case SDL_PIXELFORMAT_NV21:
   817             pixfmt = MTLPixelFormatR8Unorm;
   818             break;
   819         default:
   820             return SDL_SetError("Texture format %s not supported by Metal", SDL_GetPixelFormatName(texture->format));
   821     }
   822 
   823     MTLTextureDescriptor *mtltexdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:pixfmt
   824                                             width:(NSUInteger)texture->w height:(NSUInteger)texture->h mipmapped:NO];
   825 
   826     /* Not available in iOS 8. */
   827     if ([mtltexdesc respondsToSelector:@selector(usage)]) {
   828         if (texture->access == SDL_TEXTUREACCESS_TARGET) {
   829             mtltexdesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
   830         } else {
   831             mtltexdesc.usage = MTLTextureUsageShaderRead;
   832         }
   833     }
   834     
   835     id<MTLTexture> mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
   836     if (mtltexture == nil) {
   837         return SDL_SetError("Texture allocation failed");
   838     }
   839 
   840     id<MTLTexture> mtltexture_uv = nil;
   841 
   842     BOOL yuv = (texture->format == SDL_PIXELFORMAT_IYUV) || (texture->format == SDL_PIXELFORMAT_YV12);
   843     BOOL nv12 = (texture->format == SDL_PIXELFORMAT_NV12) || (texture->format == SDL_PIXELFORMAT_NV21);
   844 
   845     if (yuv) {
   846         mtltexdesc.pixelFormat = MTLPixelFormatR8Unorm;
   847         mtltexdesc.width = (texture->w + 1) / 2;
   848         mtltexdesc.height = (texture->h + 1) / 2;
   849         mtltexdesc.textureType = MTLTextureType2DArray;
   850         mtltexdesc.arrayLength = 2;
   851         mtltexture_uv = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
   852     } else if (nv12) {
   853         mtltexdesc.pixelFormat = MTLPixelFormatRG8Unorm;
   854         mtltexdesc.width = (texture->w + 1) / 2;
   855         mtltexdesc.height = (texture->h + 1) / 2;
   856         mtltexture_uv = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
   857     }
   858 
   859     METAL_TextureData *texturedata = [[METAL_TextureData alloc] init];
   860     if (texture->scaleMode == SDL_ScaleModeNearest) {
   861         texturedata.mtlsampler = data.mtlsamplernearest;
   862     } else {
   863         texturedata.mtlsampler = data.mtlsamplerlinear;
   864     }
   865     texturedata.mtltexture = mtltexture;
   866     texturedata.mtltexture_uv = mtltexture_uv;
   867 
   868     texturedata.yuv = yuv;
   869     texturedata.nv12 = nv12;
   870 
   871     if (yuv) {
   872         texturedata.fragmentFunction = SDL_METAL_FRAGMENT_YUV;
   873     } else if (texture->format == SDL_PIXELFORMAT_NV12) {
   874         texturedata.fragmentFunction = SDL_METAL_FRAGMENT_NV12;
   875     } else if (texture->format == SDL_PIXELFORMAT_NV21) {
   876         texturedata.fragmentFunction = SDL_METAL_FRAGMENT_NV21;
   877     } else {
   878         texturedata.fragmentFunction = SDL_METAL_FRAGMENT_COPY;
   879     }
   880 
   881     if (yuv || nv12) {
   882         size_t offset = 0;
   883         SDL_YUV_CONVERSION_MODE mode = SDL_GetYUVConversionModeForResolution(texture->w, texture->h);
   884         switch (mode) {
   885             case SDL_YUV_CONVERSION_JPEG: offset = CONSTANTS_OFFSET_DECODE_JPEG; break;
   886             case SDL_YUV_CONVERSION_BT601: offset = CONSTANTS_OFFSET_DECODE_BT601; break;
   887             case SDL_YUV_CONVERSION_BT709: offset = CONSTANTS_OFFSET_DECODE_BT709; break;
   888             default: offset = 0; break;
   889         }
   890         texturedata.conversionBufferOffset = offset;
   891     }
   892 
   893     texture->driverdata = (void*)CFBridgingRetain(texturedata);
   894 
   895 #if !__has_feature(objc_arc)
   896     [texturedata release];
   897     [mtltexture release];
   898     [mtltexture_uv release];
   899 #endif
   900 
   901     return 0;
   902 }}
   903 
   904 static int
   905 METAL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   906                  const SDL_Rect * rect, const void *pixels, int pitch)
   907 { @autoreleasepool {
   908     METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
   909 
   910     /* !!! FIXME: replaceRegion does not do any synchronization, so it might
   911      * !!! FIXME: stomp on a previous frame's data that's currently being read
   912      * !!! FIXME: by the GPU. */
   913     [texturedata.mtltexture replaceRegion:MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h)
   914                               mipmapLevel:0
   915                                 withBytes:pixels
   916                               bytesPerRow:pitch];
   917 
   918     if (texturedata.yuv) {
   919         int Uslice = texture->format == SDL_PIXELFORMAT_YV12 ? 1 : 0;
   920         int Vslice = texture->format == SDL_PIXELFORMAT_YV12 ? 0 : 1;
   921 
   922         /* Skip to the correct offset into the next texture */
   923         pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
   924         [texturedata.mtltexture_uv replaceRegion:MTLRegionMake2D(rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2)
   925                                      mipmapLevel:0
   926                                            slice:Uslice
   927                                        withBytes:pixels
   928                                      bytesPerRow:(pitch + 1) / 2
   929                                    bytesPerImage:0];
   930 
   931         /* Skip to the correct offset into the next texture */
   932         pixels = (const void*)((const Uint8*)pixels + ((rect->h + 1) / 2) * ((pitch + 1)/2));
   933         [texturedata.mtltexture_uv replaceRegion:MTLRegionMake2D(rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2)
   934                                      mipmapLevel:0
   935                                            slice:Vslice
   936                                        withBytes:pixels
   937                                      bytesPerRow:(pitch + 1) / 2
   938                                    bytesPerImage:0];
   939     }
   940 
   941     if (texturedata.nv12) {
   942         /* Skip to the correct offset into the next texture */
   943         pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
   944         [texturedata.mtltexture_uv replaceRegion:MTLRegionMake2D(rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2)
   945                                      mipmapLevel:0
   946                                            slice:0
   947                                        withBytes:pixels
   948                                      bytesPerRow:2 * ((pitch + 1) / 2)
   949                                    bytesPerImage:0];
   950     }
   951 
   952     return 0;
   953 }}
   954 
   955 static int
   956 METAL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
   957                     const SDL_Rect * rect,
   958                     const Uint8 *Yplane, int Ypitch,
   959                     const Uint8 *Uplane, int Upitch,
   960                     const Uint8 *Vplane, int Vpitch)
   961 { @autoreleasepool {
   962     METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
   963     const int Uslice = 0;
   964     const int Vslice = 1;
   965 
   966     /* Bail out if we're supposed to update an empty rectangle */
   967     if (rect->w <= 0 || rect->h <= 0) {
   968         return 0;
   969     }
   970 
   971     [texturedata.mtltexture replaceRegion:MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h)
   972                               mipmapLevel:0
   973                                 withBytes:Yplane
   974                               bytesPerRow:Ypitch];
   975 
   976     [texturedata.mtltexture_uv replaceRegion:MTLRegionMake2D(rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2)
   977                                  mipmapLevel:0
   978                                        slice:Uslice
   979                                    withBytes:Uplane
   980                                  bytesPerRow:Upitch
   981                                bytesPerImage:0];
   982 
   983     [texturedata.mtltexture_uv replaceRegion:MTLRegionMake2D(rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2)
   984                                  mipmapLevel:0
   985                                        slice:Vslice
   986                                    withBytes:Vplane
   987                                  bytesPerRow:Vpitch
   988                                bytesPerImage:0];
   989 
   990     return 0;
   991 }}
   992 
   993 static int
   994 METAL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
   995                const SDL_Rect * rect, void **pixels, int *pitch)
   996 {
   997     return SDL_Unsupported();   // !!! FIXME: write me
   998 }
   999 
  1000 static void
  1001 METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
  1002 {
  1003     // !!! FIXME: write me
  1004 }
  1005 
  1006 static int
  1007 METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
  1008 { @autoreleasepool {
  1009     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
  1010 
  1011     if (data.mtlcmdencoder) {
  1012         /* End encoding for the previous render target so we can set up a new
  1013          * render pass for this one. */
  1014         [data.mtlcmdencoder endEncoding];
  1015         [data.mtlcmdbuffer commit];
  1016 
  1017         data.mtlcmdencoder = nil;
  1018         data.mtlcmdbuffer = nil;
  1019     }
  1020 
  1021     /* We don't begin a new render pass right away - we delay it until an actual
  1022      * draw or clear happens. That way we can use hardware clears when possible,
  1023      * which are only available when beginning a new render pass. */
  1024     return 0;
  1025 }}
  1026 
  1027 static int
  1028 METAL_SetOrthographicProjection(SDL_Renderer *renderer, int w, int h)
  1029 { @autoreleasepool {
  1030     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
  1031     float projection[4][4];
  1032 
  1033     if (!w || !h) {
  1034         return 0;
  1035     }
  1036 
  1037     /* Prepare an orthographic projection */
  1038     projection[0][0] = 2.0f / w;
  1039     projection[0][1] = 0.0f;
  1040     projection[0][2] = 0.0f;
  1041     projection[0][3] = 0.0f;
  1042     projection[1][0] = 0.0f;
  1043     projection[1][1] = -2.0f / h;
  1044     projection[1][2] = 0.0f;
  1045     projection[1][3] = 0.0f;
  1046     projection[2][0] = 0.0f;
  1047     projection[2][1] = 0.0f;
  1048     projection[2][2] = 0.0f;
  1049     projection[2][3] = 0.0f;
  1050     projection[3][0] = -1.0f;
  1051     projection[3][1] = 1.0f;
  1052     projection[3][2] = 0.0f;
  1053     projection[3][3] = 1.0f;
  1054 
  1055     // !!! FIXME: This should be in a buffer...
  1056     [data.mtlcmdencoder setVertexBytes:projection length:sizeof(float)*16 atIndex:2];
  1057     return 0;
  1058 }}
  1059 
  1060 static int
  1061 METAL_UpdateViewport(SDL_Renderer * renderer)
  1062 { @autoreleasepool {
  1063     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
  1064     if (data.mtlcmdencoder) {
  1065         MTLViewport viewport;
  1066         viewport.originX = renderer->viewport.x;
  1067         viewport.originY = renderer->viewport.y;
  1068         viewport.width = renderer->viewport.w;
  1069         viewport.height = renderer->viewport.h;
  1070         viewport.znear = 0.0;
  1071         viewport.zfar = 1.0;
  1072         [data.mtlcmdencoder setViewport:viewport];
  1073         METAL_SetOrthographicProjection(renderer, renderer->viewport.w, renderer->viewport.h);
  1074     }
  1075     return 0;
  1076 }}
  1077 
  1078 static int
  1079 METAL_UpdateClipRect(SDL_Renderer * renderer)
  1080 { @autoreleasepool {
  1081     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
  1082     if (data.mtlcmdencoder) {
  1083         MTLScissorRect mtlrect;
  1084         // !!! FIXME: should this care about the viewport?
  1085         if (renderer->clipping_enabled) {
  1086             const SDL_Rect *rect = &renderer->clip_rect;
  1087             mtlrect.x = renderer->viewport.x + rect->x;
  1088             mtlrect.y = renderer->viewport.x + rect->y;
  1089             mtlrect.width = rect->w;
  1090             mtlrect.height = rect->h;
  1091         } else {
  1092             mtlrect.x = renderer->viewport.x;
  1093             mtlrect.y = renderer->viewport.y;
  1094             mtlrect.width = renderer->viewport.w;
  1095             mtlrect.height = renderer->viewport.h;
  1096         }
  1097         if (mtlrect.width > 0 && mtlrect.height > 0) {
  1098             [data.mtlcmdencoder setScissorRect:mtlrect];
  1099         }
  1100     }
  1101     return 0;
  1102 }}
  1103 
  1104 static int
  1105 METAL_RenderClear(SDL_Renderer * renderer)
  1106 { @autoreleasepool {
  1107     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
  1108 
  1109     /* Since we set up the render command encoder lazily when a draw is
  1110      * requested, we can do the fast path hardware clear if no draws have
  1111      * happened since the last SetRenderTarget. */
  1112     if (data.mtlcmdencoder == nil) {
  1113         METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionClear);
  1114     } else {
  1115         // !!! FIXME: render color should live in a dedicated uniform buffer.
  1116         const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
  1117 
  1118         MTLViewport viewport;  // RenderClear ignores the viewport state, though, so reset that.
  1119         viewport.originX = viewport.originY = 0.0;
  1120         viewport.width = data.mtlpassdesc.colorAttachments[0].texture.width;
  1121         viewport.height = data.mtlpassdesc.colorAttachments[0].texture.height;
  1122         viewport.znear = 0.0;
  1123         viewport.zfar = 1.0;
  1124 
  1125         // Slow path for clearing: draw a filled fullscreen triangle.
  1126         METAL_SetOrthographicProjection(renderer, 1, 1);
  1127         [data.mtlcmdencoder setViewport:viewport];
  1128         [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, SDL_METAL_FRAGMENT_SOLID, SDL_BLENDMODE_NONE)];
  1129         [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_CLEAR_VERTS atIndex:0];
  1130         [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_IDENTITY atIndex:3];
  1131         [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
  1132         [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
  1133 
  1134         // reset the viewport for the rest of our usual drawing work...
  1135         viewport.originX = renderer->viewport.x;
  1136         viewport.originY = renderer->viewport.y;
  1137         viewport.width = renderer->viewport.w;
  1138         viewport.height = renderer->viewport.h;
  1139         viewport.znear = 0.0;
  1140         viewport.zfar = 1.0;
  1141         [data.mtlcmdencoder setViewport:viewport];
  1142         METAL_SetOrthographicProjection(renderer, renderer->viewport.w, renderer->viewport.h);
  1143     }
  1144 
  1145     return 0;
  1146 }}
  1147 
  1148 // normalize a value from 0.0f to len into 0.0f to 1.0f.
  1149 static inline float
  1150 normtex(const float _val, const float len)
  1151 {
  1152     return _val / len;
  1153 }
  1154 
  1155 static int
  1156 DrawVerts(SDL_Renderer * renderer, const SDL_FPoint * points, int count,
  1157           const MTLPrimitiveType primtype)
  1158 { @autoreleasepool {
  1159     METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
  1160 
  1161     const size_t vertlen = (sizeof (float) * 2) * count;
  1162     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
  1163 
  1164     // !!! FIXME: render color should live in a dedicated uniform buffer.
  1165     const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
  1166 
  1167     [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, SDL_METAL_FRAGMENT_SOLID, renderer->blendMode)];
  1168     [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
  1169 
  1170     [data.mtlcmdencoder setVertexBytes:points length:vertlen atIndex:0];
  1171     [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM atIndex:3];
  1172     [data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];
  1173 
  1174     return 0;
  1175 }}
  1176 
  1177 static int
  1178 METAL_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points, int count)
  1179 {
  1180     return DrawVerts(renderer, points, count, MTLPrimitiveTypePoint);
  1181 }
  1182 
  1183 static int
  1184 METAL_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points, int count)
  1185 {
  1186     return DrawVerts(renderer, points, count, MTLPrimitiveTypeLineStrip);
  1187 }
  1188 
  1189 static int
  1190 METAL_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count)
  1191 { @autoreleasepool {
  1192     METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
  1193     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
  1194 
  1195     // !!! FIXME: render color should live in a dedicated uniform buffer.
  1196     const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
  1197 
  1198     [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, SDL_METAL_FRAGMENT_SOLID, renderer->blendMode)];
  1199     [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
  1200     [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_IDENTITY atIndex:3];
  1201 
  1202     for (int i = 0; i < count; i++, rects++) {
  1203         if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) continue;
  1204 
  1205         const float verts[] = {
  1206             rects->x, rects->y + rects->h,
  1207             rects->x, rects->y,
  1208             rects->x + rects->w, rects->y + rects->h,
  1209             rects->x + rects->w, rects->y
  1210         };
  1211 
  1212         [data.mtlcmdencoder setVertexBytes:verts length:sizeof(verts) atIndex:0];
  1213         [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
  1214     }
  1215 
  1216     return 0;
  1217 }}
  1218 
  1219 static void
  1220 METAL_SetupRenderCopy(METAL_RenderData *data, SDL_Texture *texture, METAL_TextureData *texturedata)
  1221 {
  1222     float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
  1223     if (texture->modMode) {
  1224         color[0] = ((float)texture->r) / 255.0f;
  1225         color[1] = ((float)texture->g) / 255.0f;
  1226         color[2] = ((float)texture->b) / 255.0f;
  1227         color[3] = ((float)texture->a) / 255.0f;
  1228     }
  1229 
  1230     [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, texturedata.fragmentFunction, texture->blendMode)];
  1231     [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
  1232     [data.mtlcmdencoder setFragmentSamplerState:texturedata.mtlsampler atIndex:0];
  1233 
  1234     [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0];
  1235 
  1236     if (texturedata.yuv || texturedata.nv12) {
  1237         [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture_uv atIndex:1];
  1238         [data.mtlcmdencoder setFragmentBuffer:data.mtlbufconstants offset:texturedata.conversionBufferOffset atIndex:1];
  1239     }
  1240 }
  1241 
  1242 static int
  1243 METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
  1244               const SDL_Rect * srcrect, const SDL_FRect * dstrect)
  1245 { @autoreleasepool {
  1246     METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
  1247     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
  1248     METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
  1249     const float texw = (float) texturedata.mtltexture.width;
  1250     const float texh = (float) texturedata.mtltexture.height;
  1251 
  1252     METAL_SetupRenderCopy(data, texture, texturedata);
  1253 
  1254     const float xy[] = {
  1255         dstrect->x, dstrect->y + dstrect->h,
  1256         dstrect->x, dstrect->y,
  1257         dstrect->x + dstrect->w, dstrect->y + dstrect->h,
  1258         dstrect->x + dstrect->w, dstrect->y
  1259     };
  1260 
  1261     const float uv[] = {
  1262         normtex(srcrect->x, texw), normtex(srcrect->y + srcrect->h, texh),
  1263         normtex(srcrect->x, texw), normtex(srcrect->y, texh),
  1264         normtex(srcrect->x + srcrect->w, texw), normtex(srcrect->y + srcrect->h, texh),
  1265         normtex(srcrect->x + srcrect->w, texw), normtex(srcrect->y, texh)
  1266     };
  1267 
  1268     [data.mtlcmdencoder setVertexBytes:xy length:sizeof(xy) atIndex:0];
  1269     [data.mtlcmdencoder setVertexBytes:uv length:sizeof(uv) atIndex:1];
  1270     [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_IDENTITY atIndex:3];
  1271     [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
  1272 
  1273     return 0;
  1274 }}
  1275 
  1276 static int
  1277 METAL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
  1278               const SDL_Rect * srcrect, const SDL_FRect * dstrect,
  1279               const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
  1280 { @autoreleasepool {
  1281     METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
  1282     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
  1283     METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
  1284     const float texw = (float) texturedata.mtltexture.width;
  1285     const float texh = (float) texturedata.mtltexture.height;
  1286     float transform[16];
  1287     float minu, maxu, minv, maxv;
  1288 
  1289     METAL_SetupRenderCopy(data, texture, texturedata);
  1290 
  1291     minu = normtex(srcrect->x, texw);
  1292     maxu = normtex(srcrect->x + srcrect->w, texw);
  1293     minv = normtex(srcrect->y, texh);
  1294     maxv = normtex(srcrect->y + srcrect->h, texh);
  1295 
  1296     if (flip & SDL_FLIP_HORIZONTAL) {
  1297         float tmp = maxu;
  1298         maxu = minu;
  1299         minu = tmp;
  1300     }
  1301     if (flip & SDL_FLIP_VERTICAL) {
  1302         float tmp = maxv;
  1303         maxv = minv;
  1304         minv = tmp;
  1305     }
  1306 
  1307     const float uv[] = {
  1308         minu, maxv,
  1309         minu, minv,
  1310         maxu, maxv,
  1311         maxu, minv
  1312     };
  1313 
  1314     const float xy[] = {
  1315         -center->x, dstrect->h - center->y,
  1316         -center->x, -center->y,
  1317         dstrect->w - center->x, dstrect->h - center->y,
  1318         dstrect->w - center->x, -center->y
  1319     };
  1320 
  1321     {
  1322         float rads = (float)(M_PI * (float) angle / 180.0f);
  1323         float c = cosf(rads), s = sinf(rads);
  1324         SDL_memset(transform, 0, sizeof(transform));
  1325 
  1326         transform[10] = transform[15] = 1.0f;
  1327 
  1328         /* Rotation */
  1329         transform[0]  = c;
  1330         transform[1]  = s;
  1331         transform[4]  = -s;
  1332         transform[5]  = c;
  1333 
  1334         /* Translation */
  1335         transform[12] = dstrect->x + center->x;
  1336         transform[13] = dstrect->y + center->y;
  1337     }
  1338 
  1339     [data.mtlcmdencoder setVertexBytes:xy length:sizeof(xy) atIndex:0];
  1340     [data.mtlcmdencoder setVertexBytes:uv length:sizeof(uv) atIndex:1];
  1341     [data.mtlcmdencoder setVertexBytes:transform length:sizeof(transform) atIndex:3];
  1342     [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
  1343 
  1344     return 0;
  1345 }}
  1346 
  1347 static int
  1348 METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
  1349                     Uint32 pixel_format, void * pixels, int pitch)
  1350 { @autoreleasepool {
  1351     METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
  1352 
  1353     // !!! FIXME: this probably needs to commit the current command buffer, and probably waitUntilCompleted
  1354     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
  1355     id<MTLTexture> mtltexture = data.mtlpassdesc.colorAttachments[0].texture;
  1356     MTLRegion mtlregion = MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h);
  1357 
  1358     // we only do BGRA8 or RGBA8 at the moment, so 4 will do.
  1359     const int temp_pitch = rect->w * 4;
  1360     void *temp_pixels = SDL_malloc(temp_pitch * rect->h);
  1361     if (!temp_pixels) {
  1362         return SDL_OutOfMemory();
  1363     }
  1364 
  1365     [mtltexture getBytes:temp_pixels bytesPerRow:temp_pitch fromRegion:mtlregion mipmapLevel:0];
  1366 
  1367     const Uint32 temp_format = (mtltexture.pixelFormat == MTLPixelFormatBGRA8Unorm) ? SDL_PIXELFORMAT_ARGB8888 : SDL_PIXELFORMAT_ABGR8888;
  1368     const int status = SDL_ConvertPixels(rect->w, rect->h, temp_format, temp_pixels, temp_pitch, pixel_format, pixels, pitch);
  1369     SDL_free(temp_pixels);
  1370     return status;
  1371 }}
  1372 
  1373 static void
  1374 METAL_RenderPresent(SDL_Renderer * renderer)
  1375 { @autoreleasepool {
  1376     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
  1377 
  1378     if (data.mtlcmdencoder != nil) {
  1379         [data.mtlcmdencoder endEncoding];
  1380     }
  1381     if (data.mtlbackbuffer != nil) {
  1382         [data.mtlcmdbuffer presentDrawable:data.mtlbackbuffer];
  1383     }
  1384     if (data.mtlcmdbuffer != nil) {
  1385         [data.mtlcmdbuffer commit];
  1386     }
  1387     data.mtlcmdencoder = nil;
  1388     data.mtlcmdbuffer = nil;
  1389     data.mtlbackbuffer = nil;
  1390 }}
  1391 
  1392 static void
  1393 METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
  1394 { @autoreleasepool {
  1395     CFBridgingRelease(texture->driverdata);
  1396     texture->driverdata = NULL;
  1397 }}
  1398 
  1399 static void
  1400 METAL_DestroyRenderer(SDL_Renderer * renderer)
  1401 { @autoreleasepool {
  1402     if (renderer->driverdata) {
  1403         METAL_RenderData *data = CFBridgingRelease(renderer->driverdata);
  1404 
  1405         if (data.mtlcmdencoder != nil) {
  1406             [data.mtlcmdencoder endEncoding];
  1407         }
  1408 
  1409         DestroyAllPipelines(data.allpipelines, data.pipelinescount);
  1410     }
  1411 
  1412     SDL_free(renderer);
  1413 }}
  1414 
  1415 static void *
  1416 METAL_GetMetalLayer(SDL_Renderer * renderer)
  1417 { @autoreleasepool {
  1418     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
  1419     return (__bridge void*)data.mtllayer;
  1420 }}
  1421 
  1422 static void *
  1423 METAL_GetMetalCommandEncoder(SDL_Renderer * renderer)
  1424 { @autoreleasepool {
  1425     METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
  1426     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
  1427     return (__bridge void*)data.mtlcmdencoder;
  1428 }}
  1429 
  1430 #endif /* SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED */
  1431 
  1432 /* vi: set ts=4 sw=4 expandtab: */