src/render/metal/SDL_render_metal.m
changeset 11732 ad13456d6e7f
parent 11730 ac6c607e065c
child 11733 490588c02a65
equal deleted inserted replaced
11731:30f337dc8c74 11732:ad13456d6e7f
    26 #include "SDL_log.h"
    26 #include "SDL_log.h"
    27 #include "SDL_assert.h"
    27 #include "SDL_assert.h"
    28 #include "SDL_syswm.h"
    28 #include "SDL_syswm.h"
    29 #include "../SDL_sysrender.h"
    29 #include "../SDL_sysrender.h"
    30 
    30 
       
    31 #ifdef __MACOSX__
    31 #include <Cocoa/Cocoa.h>
    32 #include <Cocoa/Cocoa.h>
       
    33 #else
       
    34 #include "../../video/uikit/SDL_uikitmetalview.h"
       
    35 #endif
    32 #include <Metal/Metal.h>
    36 #include <Metal/Metal.h>
    33 #include <QuartzCore/CAMetalLayer.h>
    37 #include <QuartzCore/CAMetalLayer.h>
    34 
    38 
    35 /* Regenerate these with build-metal-shaders.sh */
    39 /* Regenerate these with build-metal-shaders.sh */
    36 #ifdef __MACOSX__
    40 #ifdef __MACOSX__
    87      {SDL_PIXELFORMAT_ARGB8888, SDL_PIXELFORMAT_ABGR8888},
    91      {SDL_PIXELFORMAT_ARGB8888, SDL_PIXELFORMAT_ABGR8888},
    88      4096,  // !!! FIXME: how do you query Metal for this?
    92      4096,  // !!! FIXME: how do you query Metal for this?
    89      4096}
    93      4096}
    90 };
    94 };
    91 
    95 
    92 typedef struct METAL_BufferList
    96 @interface METAL_RenderData : NSObject
    93 {
    97     @property (atomic, retain) id<MTLDevice> mtldevice;
    94     id<MTLBuffer> mtlbuffer;
    98     @property (atomic, retain) id<MTLCommandQueue> mtlcmdqueue;
    95     struct METAL_BufferPool *next;
    99     @property (atomic, retain) id<MTLCommandBuffer> mtlcmdbuffer;
    96 } METAL_BufferList;
   100     @property (atomic, retain) id<MTLRenderCommandEncoder> mtlcmdencoder;
    97 
   101     @property (atomic, retain) id<MTLLibrary> mtllibrary;
    98 typedef struct
   102     @property (atomic, retain) id<CAMetalDrawable> mtlbackbuffer;
    99 {
   103     @property (atomic, retain) NSMutableArray *mtlpipelineprims;
   100     id<MTLDevice> mtldevice;
   104     @property (atomic, retain) NSMutableArray *mtlpipelinecopy;
   101     id<MTLCommandQueue> mtlcmdqueue;
   105     @property (atomic, retain) id<MTLBuffer> mtlbufclearverts;
   102     id<MTLCommandBuffer> mtlcmdbuffer;
   106     @property (atomic, retain) CAMetalLayer *mtllayer;
   103     id<MTLRenderCommandEncoder> mtlcmdencoder;
   107     @property (atomic, retain) MTLRenderPassDescriptor *mtlpassdesc;
   104     id<MTLLibrary> mtllibrary;
   108 @end
   105     id<CAMetalDrawable> mtlbackbuffer;
   109 
   106     id<MTLRenderPipelineState> mtlpipelineprims[4];
   110 @implementation METAL_RenderData
   107     id<MTLRenderPipelineState> mtlpipelinecopy[4];
   111 @end
   108     id<MTLBuffer> mtlbufclearverts;
       
   109     CAMetalLayer *mtllayer;
       
   110     MTLRenderPassDescriptor *mtlpassdesc;
       
   111 } METAL_RenderData;
       
   112 
       
   113 
   112 
   114 static int
   113 static int
   115 IsMetalAvailable(const SDL_SysWMinfo *syswm)
   114 IsMetalAvailable(const SDL_SysWMinfo *syswm)
   116 {
   115 {
   117     if (syswm->subsystem != SDL_SYSWM_COCOA) {  // !!! FIXME: SDL_SYSWM_UIKIT for iOS, too!
   116     if (syswm->subsystem != SDL_SYSWM_COCOA) {  // !!! FIXME: SDL_SYSWM_UIKIT for iOS, too!
   130 
   129 
   131 static id<MTLRenderPipelineState>
   130 static id<MTLRenderPipelineState>
   132 MakePipelineState(METAL_RenderData *data, NSString *label, NSString *vertfn,
   131 MakePipelineState(METAL_RenderData *data, NSString *label, NSString *vertfn,
   133                   NSString *fragfn, const SDL_BlendMode blendmode)
   132                   NSString *fragfn, const SDL_BlendMode blendmode)
   134 {
   133 {
   135     id<MTLFunction> mtlvertfn = [data->mtllibrary newFunctionWithName:vertfn];
   134     id<MTLFunction> mtlvertfn = [data.mtllibrary newFunctionWithName:vertfn];
   136     id<MTLFunction> mtlfragfn = [data->mtllibrary newFunctionWithName:fragfn];
   135     id<MTLFunction> mtlfragfn = [data.mtllibrary newFunctionWithName:fragfn];
   137     SDL_assert(mtlvertfn != nil);
   136     SDL_assert(mtlvertfn != nil);
   138     SDL_assert(mtlfragfn != nil);
   137     SDL_assert(mtlfragfn != nil);
   139 
   138 
   140     MTLRenderPipelineDescriptor *mtlpipedesc = [[MTLRenderPipelineDescriptor alloc] init];
   139     MTLRenderPipelineDescriptor *mtlpipedesc = [[MTLRenderPipelineDescriptor alloc] init];
   141     mtlpipedesc.vertexFunction = mtlvertfn;
   140     mtlpipedesc.vertexFunction = mtlvertfn;
   142     mtlpipedesc.fragmentFunction = mtlfragfn;
   141     mtlpipedesc.fragmentFunction = mtlfragfn;
   143     mtlpipedesc.colorAttachments[0].pixelFormat = data->mtlbackbuffer.texture.pixelFormat;
   142     mtlpipedesc.colorAttachments[0].pixelFormat = data.mtlbackbuffer.texture.pixelFormat;
   144 
   143 
   145     switch (blendmode) {
   144     switch (blendmode) {
   146         case SDL_BLENDMODE_BLEND:
   145         case SDL_BLENDMODE_BLEND:
   147             mtlpipedesc.colorAttachments[0].blendingEnabled = YES;
   146             mtlpipedesc.colorAttachments[0].blendingEnabled = YES;
   148             mtlpipedesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
   147             mtlpipedesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
   179     }
   178     }
   180 
   179 
   181     mtlpipedesc.label = label;
   180     mtlpipedesc.label = label;
   182 
   181 
   183     NSError *err = nil;
   182     NSError *err = nil;
   184     id<MTLRenderPipelineState> retval = [data->mtldevice newRenderPipelineStateWithDescriptor:mtlpipedesc error:&err];
   183     id<MTLRenderPipelineState> retval = [data.mtldevice newRenderPipelineStateWithDescriptor:mtlpipedesc error:&err];
   185     SDL_assert(err == nil);
   184     SDL_assert(err == nil);
       
   185 #if !__has_feature(objc_arc)
   186     [mtlpipedesc release];  // !!! FIXME: can these be reused for each creation, or does the pipeline obtain it?
   186     [mtlpipedesc release];  // !!! FIXME: can these be reused for each creation, or does the pipeline obtain it?
   187     [mtlvertfn release];
   187     [mtlvertfn release];
   188     [mtlfragfn release];
   188     [mtlfragfn release];
   189     [label release];
   189     [label release];
   190 
   190 #endif
   191     return retval;
   191     return retval;
   192 }
   192 }
   193 
   193 
   194 static void
   194 static void
   195 MakePipelineStates(METAL_RenderData *data, id<MTLRenderPipelineState> *states,
   195 MakePipelineStates(METAL_RenderData *data, NSMutableArray *states,
   196                    NSString *label, NSString *vertfn, NSString *fragfn)
   196                    NSString *label, NSString *vertfn, NSString *fragfn)
   197 {
   197 {
   198     int i = 0;
   198     [states addObject:MakePipelineState(data, [label stringByAppendingString:@" (blendmode=none)"], vertfn, fragfn, SDL_BLENDMODE_NONE)];
   199     states[i++] = MakePipelineState(data, [label stringByAppendingString:@" (blendmode=none)"], vertfn, fragfn, SDL_BLENDMODE_NONE);
   199     [states addObject:MakePipelineState(data, [label stringByAppendingString:@" (blendmode=blend)"], vertfn, fragfn, SDL_BLENDMODE_BLEND)];
   200     states[i++] = MakePipelineState(data, [label stringByAppendingString:@" (blendmode=blend)"], vertfn, fragfn, SDL_BLENDMODE_BLEND);
   200     [states addObject:MakePipelineState(data, [label stringByAppendingString:@" (blendmode=add)"], vertfn, fragfn, SDL_BLENDMODE_ADD)];
   201     states[i++] = MakePipelineState(data, [label stringByAppendingString:@" (blendmode=add)"], vertfn, fragfn, SDL_BLENDMODE_ADD);
   201     [states addObject:MakePipelineState(data, [label stringByAppendingString:@" (blendmode=mod)"], vertfn, fragfn, SDL_BLENDMODE_MOD)];
   202     states[i++] = MakePipelineState(data, [label stringByAppendingString:@" (blendmode=mod)"], vertfn, fragfn, SDL_BLENDMODE_MOD);
       
   203 }
   202 }
   204 
   203 
   205 static inline id<MTLRenderPipelineState>
   204 static inline id<MTLRenderPipelineState>
   206 ChoosePipelineState(id<MTLRenderPipelineState> *states, const SDL_BlendMode blendmode)
   205 ChoosePipelineState(NSMutableArray *states, const SDL_BlendMode blendmode)
   207 {
   206 {
   208     switch (blendmode) {
   207     switch (blendmode) {
   209         case SDL_BLENDMODE_BLEND: return states[1];
   208         case SDL_BLENDMODE_BLEND: return (id<MTLRenderPipelineState>)states[1];
   210         case SDL_BLENDMODE_ADD: return states[2];
   209         case SDL_BLENDMODE_ADD: return (id<MTLRenderPipelineState>)states[2];
   211         case SDL_BLENDMODE_MOD: return states[3];
   210         case SDL_BLENDMODE_MOD: return (id<MTLRenderPipelineState>)states[3];
   212         default: return states[0];
   211         default: return (id<MTLRenderPipelineState>)states[0];
   213     }
   212     }
   214     return nil;
   213     return nil;
   215 }
   214 }
   216 
   215 
   217 static SDL_Renderer *
   216 static SDL_Renderer *
   228 
   227 
   229     if (IsMetalAvailable(&syswm) == -1) {
   228     if (IsMetalAvailable(&syswm) == -1) {
   230         return NULL;
   229         return NULL;
   231     }
   230     }
   232 
   231 
   233     data = (METAL_RenderData *) SDL_calloc(1, sizeof(*data));
   232     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
   234     if (!data) {
   233     if (!renderer) {
   235         SDL_OutOfMemory();
   234         SDL_OutOfMemory();
   236         return NULL;
   235         return NULL;
   237     }
   236     }
   238 
   237 
   239     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
   238     data = [[METAL_RenderData alloc] init];
   240     if (!renderer) {
   239 
   241         SDL_free(data);
   240 #if __has_feature(objc_arc)
   242         SDL_OutOfMemory();
   241     renderer->driverdata = (void*)CFBridgingRetain(data);
   243         return NULL;
   242 #else
   244     }
       
   245 
       
   246     renderer->driverdata = data;
   243     renderer->driverdata = data;
       
   244 #endif
   247     renderer->window = window;
   245     renderer->window = window;
   248 
   246 
   249 #ifdef __MACOSX__
   247 #ifdef __MACOSX__
   250     id<MTLDevice> mtldevice = MTLCreateSystemDefaultDevice();  // !!! FIXME: MTLCopyAllDevices() can find other GPUs...
   248     id<MTLDevice> mtldevice = MTLCreateSystemDefaultDevice();  // !!! FIXME: MTLCopyAllDevices() can find other GPUs...
   251     if (mtldevice == nil) {
   249     if (mtldevice == nil) {
   252         SDL_free(renderer);
   250         SDL_free(renderer);
   253         SDL_free(data);
   251 #if !__has_feature(objc_arc)
       
   252         [data release];
       
   253 #endif
   254         SDL_SetError("Failed to obtain Metal device");
   254         SDL_SetError("Failed to obtain Metal device");
   255         return NULL;
   255         return NULL;
   256     }
   256     }
   257 
   257 
   258     // !!! FIXME: error checking on all of this.
   258     // !!! FIXME: error checking on all of this.
   271     [nsview setWantsLayer:YES];
   271     [nsview setWantsLayer:YES];
   272     [nsview setLayer:layer];
   272     [nsview setLayer:layer];
   273 
   273 
   274     [layer retain];
   274     [layer retain];
   275 #else
   275 #else
   276 
   276     UIView *view = UIKit_Mtl_AddMetalView(window);
   277 #endif
   277     CAMetalLayer *layer = (CAMetalLayer *)[view layer];
   278 
   278 #endif
   279     data->mtldevice = layer.device;
   279 
   280     data->mtllayer = layer;
   280     data.mtldevice = layer.device;
   281     data->mtlcmdqueue = [data->mtldevice newCommandQueue];
   281     data.mtllayer = layer;
   282     data->mtlcmdqueue.label = @"SDL Metal Renderer";
   282     data.mtlcmdqueue = [data.mtldevice newCommandQueue];
   283 
   283     data.mtlcmdqueue.label = @"SDL Metal Renderer";
   284     data->mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor];  // !!! FIXME: is this autoreleased?
   284 
       
   285     data.mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor];  // !!! FIXME: is this autoreleased?
   285 
   286 
   286     // we don't specify a depth or stencil buffer because the render API doesn't currently use them.
   287     // we don't specify a depth or stencil buffer because the render API doesn't currently use them.
   287     MTLRenderPassColorAttachmentDescriptor *colorAttachment = data->mtlpassdesc.colorAttachments[0];
   288     MTLRenderPassColorAttachmentDescriptor *colorAttachment = data.mtlpassdesc.colorAttachments[0];
   288     data->mtlbackbuffer = [data->mtllayer nextDrawable];
   289     data.mtlbackbuffer = [data.mtllayer nextDrawable];
   289     colorAttachment.texture = data->mtlbackbuffer.texture;
   290     colorAttachment.texture = data.mtlbackbuffer.texture;
   290     colorAttachment.loadAction = MTLLoadActionClear;
   291     colorAttachment.loadAction = MTLLoadActionClear;
   291     colorAttachment.clearColor = MTLClearColorMake(0.0f, 0.0f, 0.0f, 1.0f);
   292     colorAttachment.clearColor = MTLClearColorMake(0.0f, 0.0f, 0.0f, 1.0f);
   292     data->mtlcmdbuffer = [data->mtlcmdqueue commandBuffer];
   293     data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
   293 
   294 
   294     // Just push a clear to the screen to start so we're in a good state.
   295     // Just push a clear to the screen to start so we're in a good state.
   295     data->mtlcmdencoder = [data->mtlcmdbuffer renderCommandEncoderWithDescriptor:data->mtlpassdesc];
   296     data.mtlcmdencoder = [data.mtlcmdbuffer renderCommandEncoderWithDescriptor:data.mtlpassdesc];
   296     data->mtlcmdencoder.label = @"Initial drawable clear";
   297     data.mtlcmdencoder.label = @"Initial drawable clear";
   297 
   298 
   298     METAL_RenderPresent(renderer);
   299     METAL_RenderPresent(renderer);
   299 
   300 
   300     renderer->WindowEvent = METAL_WindowEvent;
   301     renderer->WindowEvent = METAL_WindowEvent;
   301     renderer->GetOutputSize = METAL_GetOutputSize;
   302     renderer->GetOutputSize = METAL_GetOutputSize;
   327     NSError *err = nil;
   328     NSError *err = nil;
   328 
   329 
   329     // The compiled .metallib is embedded in a static array in a header file
   330     // The compiled .metallib is embedded in a static array in a header file
   330     // but the original shader source code is in SDL_shaders_metal.metal.
   331     // but the original shader source code is in SDL_shaders_metal.metal.
   331     dispatch_data_t mtllibdata = dispatch_data_create(sdl_metallib, sdl_metallib_len, dispatch_get_global_queue(0, 0), ^{});
   332     dispatch_data_t mtllibdata = dispatch_data_create(sdl_metallib, sdl_metallib_len, dispatch_get_global_queue(0, 0), ^{});
   332     data->mtllibrary = [data->mtldevice newLibraryWithData:mtllibdata error:&err];
   333     data.mtllibrary = [data.mtldevice newLibraryWithData:mtllibdata error:&err];
   333     SDL_assert(err == nil);
   334     SDL_assert(err == nil);
       
   335 #if !__has_feature(objc_arc)
   334     dispatch_release(mtllibdata);
   336     dispatch_release(mtllibdata);
   335     data->mtllibrary.label = @"SDL Metal renderer shader library";
   337 #endif
   336 
   338     data.mtllibrary.label = @"SDL Metal renderer shader library";
   337     MakePipelineStates(data, data->mtlpipelineprims, @"SDL primitives pipeline", @"SDL_Simple_vertex", @"SDL_Simple_fragment");
   339 
   338     MakePipelineStates(data, data->mtlpipelinecopy, @"SDL_RenderCopy pipeline", @"SDL_Copy_vertex", @"SDL_Copy_fragment");
   340     data.mtlpipelineprims = [[NSMutableArray alloc] init];
       
   341     MakePipelineStates(data, data.mtlpipelineprims, @"SDL primitives pipeline", @"SDL_Simple_vertex", @"SDL_Simple_fragment");
       
   342     data.mtlpipelinecopy = [[NSMutableArray alloc] init];
       
   343     MakePipelineStates(data, data.mtlpipelinecopy, @"SDL_RenderCopy pipeline", @"SDL_Copy_vertex", @"SDL_Copy_fragment");
   339 
   344 
   340     static const float clearverts[] = { -1, -1, -1, 1, 1, 1, 1, -1, -1, -1 };
   345     static const float clearverts[] = { -1, -1, -1, 1, 1, 1, 1, -1, -1, -1 };
   341     data->mtlbufclearverts = [data->mtldevice newBufferWithBytes:clearverts length:sizeof(clearverts) options:MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModePrivate];
   346     data.mtlbufclearverts = [data.mtldevice newBufferWithBytes:clearverts length:sizeof(clearverts) options:MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModePrivate];
   342     data->mtlbufclearverts.label = @"SDL_RenderClear vertices";
   347     data.mtlbufclearverts.label = @"SDL_RenderClear vertices";
   343 
   348 
   344     // !!! FIXME: force more clears here so all the drawables are sane to start, and our static buffers are definitely flushed.
   349     // !!! FIXME: force more clears here so all the drawables are sane to start, and our static buffers are definitely flushed.
   345 
   350 
   346     return renderer;
   351     return renderer;
   347 }
   352 }
   357 }
   362 }
   358 
   363 
   359 static int
   364 static int
   360 METAL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
   365 METAL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
   361 {
   366 {
   362     METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
   367     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
   363     *w = (int) data->mtlbackbuffer.texture.width;
   368     *w = (int) data.mtlbackbuffer.texture.width;
   364     *h = (int) data->mtlbackbuffer.texture.height;
   369     *h = (int) data.mtlbackbuffer.texture.height;
   365     return 0;
   370     return 0;
   366 }
   371 }
   367 
   372 
   368 static int
   373 static int
   369 METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   374 METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   370 {
   375 {
   371     METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
   376     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
   372     MTLPixelFormat mtlpixfmt;
   377     MTLPixelFormat mtlpixfmt;
   373 
   378 
   374     switch (texture->format) {
   379     switch (texture->format) {
   375         case SDL_PIXELFORMAT_ABGR8888: mtlpixfmt = MTLPixelFormatRGBA8Unorm; break;
   380         case SDL_PIXELFORMAT_ABGR8888: mtlpixfmt = MTLPixelFormatRGBA8Unorm; break;
   376         case SDL_PIXELFORMAT_ARGB8888: mtlpixfmt = MTLPixelFormatBGRA8Unorm; break;
   381         case SDL_PIXELFORMAT_ARGB8888: mtlpixfmt = MTLPixelFormatBGRA8Unorm; break;
   379 
   384 
   380     // !!! FIXME: autorelease or nah?
   385     // !!! FIXME: autorelease or nah?
   381     MTLTextureDescriptor *mtltexdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:mtlpixfmt
   386     MTLTextureDescriptor *mtltexdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:mtlpixfmt
   382                                             width:(NSUInteger)texture->w height:(NSUInteger)texture->h mipmapped:NO];
   387                                             width:(NSUInteger)texture->w height:(NSUInteger)texture->h mipmapped:NO];
   383 
   388 
   384     id<MTLTexture> mtltexture = [data->mtldevice newTextureWithDescriptor:mtltexdesc];
   389     id<MTLTexture> mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
       
   390 #if !__has_feature(objc_arc)
   385     [mtltexdesc release];
   391     [mtltexdesc release];
       
   392 #endif
   386     if (mtltexture == nil) {
   393     if (mtltexture == nil) {
   387         return SDL_SetError("Texture allocation failed");
   394         return SDL_SetError("Texture allocation failed");
   388     }
   395     }
   389 
   396 
   390     texture->driverdata = mtltexture;
   397     texture->driverdata = (void*)CFBridgingRetain(mtltexture);
   391 
   398 
   392     return 0;
   399     return 0;
   393 }
   400 }
   394 
   401 
   395 static int
   402 static int
   398 {
   405 {
   399     // !!! FIXME: this is a synchronous call; it doesn't return until data is uploaded in some form.
   406     // !!! FIXME: this is a synchronous call; it doesn't return until data is uploaded in some form.
   400     // !!! FIXME:  Maybe move this off to a thread that marks the texture as uploaded and only stall the main thread if we try to
   407     // !!! FIXME:  Maybe move this off to a thread that marks the texture as uploaded and only stall the main thread if we try to
   401     // !!! FIXME:  use this texture before the marking is done? Is it worth it? Or will we basically always be uploading a bunch of
   408     // !!! FIXME:  use this texture before the marking is done? Is it worth it? Or will we basically always be uploading a bunch of
   402     // !!! FIXME:  stuff way ahead of time and/or using it immediately after upload?
   409     // !!! FIXME:  stuff way ahead of time and/or using it immediately after upload?
   403     id<MTLTexture> mtltexture = (id<MTLTexture>) texture->driverdata;
   410     id<MTLTexture> mtltexture = (__bridge id<MTLTexture>) texture->driverdata;
   404     [mtltexture replaceRegion:MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h) mipmapLevel:0 withBytes:pixels bytesPerRow:pitch];
   411     [mtltexture replaceRegion:MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h) mipmapLevel:0 withBytes:pixels bytesPerRow:pitch];
   405     return 0;
   412     return 0;
   406 }
   413 }
   407 
   414 
   408 static int
   415 static int
   429 }
   436 }
   430 
   437 
   431 static int
   438 static int
   432 METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
   439 METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
   433 {
   440 {
   434     METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
   441     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
   435     id<MTLTexture> mtltexture = texture ? (id<MTLTexture>) texture->driverdata : nil;
   442     id<MTLTexture> mtltexture = texture ? (__bridge id<MTLTexture>) texture->driverdata : nil;
   436     data->mtlpassdesc.colorAttachments[0].texture = mtltexture;
   443     data.mtlpassdesc.colorAttachments[0].texture = mtltexture;
   437     return 0;
   444     return 0;
   438 }
   445 }
   439 
   446 
   440 static int
   447 static int
   441 METAL_UpdateViewport(SDL_Renderer * renderer)
   448 METAL_UpdateViewport(SDL_Renderer * renderer)
   442 {
   449 {
   443     METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
   450     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
   444     if (data->mtlcmdencoder != nil) {
   451     if (data.mtlcmdencoder != nil) {
   445         MTLViewport viewport;
   452         MTLViewport viewport;
   446         viewport.originX = renderer->viewport.x;
   453         viewport.originX = renderer->viewport.x;
   447         viewport.originY = renderer->viewport.y;
   454         viewport.originY = renderer->viewport.y;
   448         viewport.width = renderer->viewport.w;
   455         viewport.width = renderer->viewport.w;
   449         viewport.height = renderer->viewport.h;
   456         viewport.height = renderer->viewport.h;
   450         viewport.znear = 0.0;
   457         viewport.znear = 0.0;
   451         viewport.zfar = 1.0;
   458         viewport.zfar = 1.0;
   452         [data->mtlcmdencoder setViewport:viewport];
   459         [data.mtlcmdencoder setViewport:viewport];
   453     }
   460     }
   454     return 0;
   461     return 0;
   455 }
   462 }
   456 
   463 
   457 static int
   464 static int
   458 METAL_UpdateClipRect(SDL_Renderer * renderer)
   465 METAL_UpdateClipRect(SDL_Renderer * renderer)
   459 {
   466 {
   460     // !!! FIXME: should this care about the viewport?
   467     // !!! FIXME: should this care about the viewport?
   461     METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
   468     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
   462     if (data->mtlcmdencoder != nil) {
   469     if (data.mtlcmdencoder != nil) {
   463         MTLScissorRect mtlrect;
   470         MTLScissorRect mtlrect;
   464         if (renderer->clipping_enabled) {
   471         if (renderer->clipping_enabled) {
   465             const SDL_Rect *rect = &renderer->clip_rect;
   472             const SDL_Rect *rect = &renderer->clip_rect;
   466             mtlrect.x = renderer->viewport.x + rect->x;
   473             mtlrect.x = renderer->viewport.x + rect->x;
   467             mtlrect.y = renderer->viewport.x + rect->y;
   474             mtlrect.y = renderer->viewport.x + rect->y;
   471             mtlrect.x = renderer->viewport.x;
   478             mtlrect.x = renderer->viewport.x;
   472             mtlrect.y = renderer->viewport.y;
   479             mtlrect.y = renderer->viewport.y;
   473             mtlrect.width = renderer->viewport.w;
   480             mtlrect.width = renderer->viewport.w;
   474             mtlrect.height = renderer->viewport.h;
   481             mtlrect.height = renderer->viewport.h;
   475         }
   482         }
   476         [data->mtlcmdencoder setScissorRect:mtlrect];
   483         [data.mtlcmdencoder setScissorRect:mtlrect];
   477     }
   484     }
   478     return 0;
   485     return 0;
   479 }
   486 }
   480 
   487 
   481 static int
   488 static int
   482 METAL_RenderClear(SDL_Renderer * renderer)
   489 METAL_RenderClear(SDL_Renderer * renderer)
   483 {
   490 {
   484     // We could dump the command buffer and force a clear on a new one, but this will respect the scissor state.
   491     // We could dump the command buffer and force a clear on a new one, but this will respect the scissor state.
   485     METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
   492     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
   486 
   493 
   487     // !!! FIXME: render color should live in a dedicated uniform buffer.
   494     // !!! FIXME: render color should live in a dedicated uniform buffer.
   488     const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
   495     const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
   489 
   496 
   490     MTLViewport viewport;  // RenderClear ignores the viewport state, though, so reset that.
   497     MTLViewport viewport;  // RenderClear ignores the viewport state, though, so reset that.
   491     viewport.originX = viewport.originY = 0.0;
   498     viewport.originX = viewport.originY = 0.0;
   492     viewport.width = data->mtlbackbuffer.texture.width;
   499     viewport.width = data.mtlbackbuffer.texture.width;
   493     viewport.height = data->mtlbackbuffer.texture.height;
   500     viewport.height = data.mtlbackbuffer.texture.height;
   494     viewport.znear = 0.0;
   501     viewport.znear = 0.0;
   495     viewport.zfar = 1.0;
   502     viewport.zfar = 1.0;
   496 
   503 
   497     // Draw as if we're doing a simple filled rect to the screen now.
   504     // Draw as if we're doing a simple filled rect to the screen now.
   498     [data->mtlcmdencoder setViewport:viewport];
   505     [data.mtlcmdencoder setViewport:viewport];
   499     [data->mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data->mtlpipelineprims, renderer->blendMode)];
   506     [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
   500     [data->mtlcmdencoder setVertexBuffer:data->mtlbufclearverts offset:0 atIndex:0];
   507     [data.mtlcmdencoder setVertexBuffer:data.mtlbufclearverts offset:0 atIndex:0];
   501     [data->mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
   508     [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
   502     [data->mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:5];
   509     [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:5];
   503 
   510 
   504     // reset the viewport for the rest of our usual drawing work...
   511     // reset the viewport for the rest of our usual drawing work...
   505     viewport.originX = renderer->viewport.x;
   512     viewport.originX = renderer->viewport.x;
   506     viewport.originY = renderer->viewport.y;
   513     viewport.originY = renderer->viewport.y;
   507     viewport.width = renderer->viewport.w;
   514     viewport.width = renderer->viewport.w;
   508     viewport.height = renderer->viewport.h;
   515     viewport.height = renderer->viewport.h;
   509     viewport.znear = 0.0;
   516     viewport.znear = 0.0;
   510     viewport.zfar = 1.0;
   517     viewport.zfar = 1.0;
   511     [data->mtlcmdencoder setViewport:viewport];
   518     [data.mtlcmdencoder setViewport:viewport];
   512 
   519 
   513     return 0;
   520     return 0;
   514 }
   521 }
   515 
   522 
   516 // normalize a value from 0.0f to len into -1.0f to 1.0f.
   523 // normalize a value from 0.0f to len into -1.0f to 1.0f.
   544     float *verts = SDL_malloc(vertlen);
   551     float *verts = SDL_malloc(vertlen);
   545     if (!verts) {
   552     if (!verts) {
   546         return SDL_OutOfMemory();
   553         return SDL_OutOfMemory();
   547     }
   554     }
   548 
   555 
   549     METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
   556     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
   550 
   557 
   551     // !!! FIXME: render color should live in a dedicated uniform buffer.
   558     // !!! FIXME: render color should live in a dedicated uniform buffer.
   552     const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
   559     const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
   553 
   560 
   554     [data->mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data->mtlpipelineprims, renderer->blendMode)];
   561     [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
   555     [data->mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
   562     [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
   556 
   563 
   557     const float w = (float) data->mtlpassdesc.colorAttachments[0].texture.width;
   564     const float w = (float) data.mtlpassdesc.colorAttachments[0].texture.width;
   558     const float h = (float) data->mtlpassdesc.colorAttachments[0].texture.height;
   565     const float h = (float) data.mtlpassdesc.colorAttachments[0].texture.height;
   559 
   566 
   560     // !!! FIXME: we can convert this in the shader. This will save the malloc and for-loop, but we still need to upload.
   567     // !!! FIXME: we can convert this in the shader. This will save the malloc and for-loop, but we still need to upload.
   561     float *ptr = verts;
   568     float *ptr = verts;
   562     for (int i = 0; i < count; i++, points++) {
   569     for (int i = 0; i < count; i++, points++) {
   563         *ptr = norm(points->x, w); ptr++;
   570         *ptr = norm(points->x, w); ptr++;
   564         *ptr = normy(points->y, h); ptr++;
   571         *ptr = normy(points->y, h); ptr++;
   565     }
   572     }
   566 
   573 
   567     [data->mtlcmdencoder setVertexBytes:verts length:vertlen atIndex:0];
   574     [data.mtlcmdencoder setVertexBytes:verts length:vertlen atIndex:0];
   568     [data->mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];
   575     [data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];
   569 
   576 
   570     SDL_free(verts);
   577     SDL_free(verts);
   571     return 0;
   578     return 0;
   572 }
   579 }
   573 
   580 
   584 }
   591 }
   585 
   592 
   586 static int
   593 static int
   587 METAL_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count)
   594 METAL_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count)
   588 {
   595 {
   589     METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
   596     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
   590 
   597 
   591     // !!! FIXME: render color should live in a dedicated uniform buffer.
   598     // !!! FIXME: render color should live in a dedicated uniform buffer.
   592     const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
   599     const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
   593 
   600 
   594     [data->mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data->mtlpipelineprims, renderer->blendMode)];
   601     [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
   595     [data->mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
   602     [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
   596 
   603 
   597     const float w = (float) data->mtlpassdesc.colorAttachments[0].texture.width;
   604     const float w = (float) data.mtlpassdesc.colorAttachments[0].texture.width;
   598     const float h = (float) data->mtlpassdesc.colorAttachments[0].texture.height;
   605     const float h = (float) data.mtlpassdesc.colorAttachments[0].texture.height;
   599 
   606 
   600     for (int i = 0; i < count; i++, rects++) {
   607     for (int i = 0; i < count; i++, rects++) {
   601         if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) continue;
   608         if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) continue;
   602 
   609 
   603         const float verts[] = {
   610         const float verts[] = {
   606             norm(rects->x + rects->w, w), normy(rects->y, h),
   613             norm(rects->x + rects->w, w), normy(rects->y, h),
   607             norm(rects->x, w), normy(rects->y + rects->h, h),
   614             norm(rects->x, w), normy(rects->y + rects->h, h),
   608             norm(rects->x + rects->w, w), normy(rects->y + rects->h, h)
   615             norm(rects->x + rects->w, w), normy(rects->y + rects->h, h)
   609         };
   616         };
   610 
   617 
   611         [data->mtlcmdencoder setVertexBytes:verts length:sizeof(verts) atIndex:0];
   618         [data.mtlcmdencoder setVertexBytes:verts length:sizeof(verts) atIndex:0];
   612         [data->mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:5];
   619         [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:5];
   613     }
   620     }
   614 
   621 
   615     return 0;
   622     return 0;
   616 }
   623 }
   617 
   624 
   618 static int
   625 static int
   619 METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
   626 METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
   620               const SDL_Rect * srcrect, const SDL_FRect * dstrect)
   627               const SDL_Rect * srcrect, const SDL_FRect * dstrect)
   621 {
   628 {
   622     METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
   629     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
   623     id<MTLTexture> mtltexture = (id<MTLTexture>) texture->driverdata;
   630     id<MTLTexture> mtltexture = (__bridge id<MTLTexture>) texture->driverdata;
   624     const float w = (float) data->mtlpassdesc.colorAttachments[0].texture.width;
   631     const float w = (float) data.mtlpassdesc.colorAttachments[0].texture.width;
   625     const float h = (float) data->mtlpassdesc.colorAttachments[0].texture.height;
   632     const float h = (float) data.mtlpassdesc.colorAttachments[0].texture.height;
   626     const float texw = (float) mtltexture.width;
   633     const float texw = (float) mtltexture.width;
   627     const float texh = (float) mtltexture.height;
   634     const float texh = (float) mtltexture.height;
   628 
   635 
   629     const float xy[] = {
   636     const float xy[] = {
   630         norm(dstrect->x, w), normy(dstrect->y + dstrect->h, h),
   637         norm(dstrect->x, w), normy(dstrect->y + dstrect->h, h),
   648         color[1] = ((float)texture->g) / 255.0f;
   655         color[1] = ((float)texture->g) / 255.0f;
   649         color[2] = ((float)texture->b) / 255.0f;
   656         color[2] = ((float)texture->b) / 255.0f;
   650         color[3] = ((float)texture->a) / 255.0f;
   657         color[3] = ((float)texture->a) / 255.0f;
   651     }
   658     }
   652 
   659 
   653     [data->mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data->mtlpipelinecopy, texture->blendMode)];
   660     [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelinecopy, texture->blendMode)];
   654     [data->mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
   661     [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
   655     [data->mtlcmdencoder setFragmentTexture:mtltexture atIndex:0];
   662     [data.mtlcmdencoder setFragmentTexture:mtltexture atIndex:0];
   656     [data->mtlcmdencoder setVertexBytes:xy length:sizeof(xy) atIndex:0];
   663     [data.mtlcmdencoder setVertexBytes:xy length:sizeof(xy) atIndex:0];
   657     [data->mtlcmdencoder setVertexBytes:uv length:sizeof(uv) atIndex:1];
   664     [data.mtlcmdencoder setVertexBytes:uv length:sizeof(uv) atIndex:1];
   658     [data->mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:5];
   665     [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:5];
   659 
   666 
   660     return 0;
   667     return 0;
   661 }
   668 }
   662 
   669 
   663 static int
   670 static int
   670 
   677 
   671 static int
   678 static int
   672 METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
   679 METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
   673                     Uint32 pixel_format, void * pixels, int pitch)
   680                     Uint32 pixel_format, void * pixels, int pitch)
   674 {
   681 {
   675     METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
   682     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
   676     MTLRenderPassColorAttachmentDescriptor *colorAttachment = data->mtlpassdesc.colorAttachments[0];
   683     MTLRenderPassColorAttachmentDescriptor *colorAttachment = data.mtlpassdesc.colorAttachments[0];
   677     id<MTLTexture> mtltexture = colorAttachment.texture;
   684     id<MTLTexture> mtltexture = colorAttachment.texture;
   678     MTLRegion mtlregion;
   685     MTLRegion mtlregion;
   679 
   686 
   680     mtlregion.origin.x = rect->x;
   687     mtlregion.origin.x = rect->x;
   681     mtlregion.origin.y = rect->y;
   688     mtlregion.origin.y = rect->y;
   700 }
   707 }
   701 
   708 
   702 static void
   709 static void
   703 METAL_RenderPresent(SDL_Renderer * renderer)
   710 METAL_RenderPresent(SDL_Renderer * renderer)
   704 {
   711 {
   705     METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
   712     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
   706     MTLRenderPassColorAttachmentDescriptor *colorAttachment = data->mtlpassdesc.colorAttachments[0];
   713     MTLRenderPassColorAttachmentDescriptor *colorAttachment = data.mtlpassdesc.colorAttachments[0];
   707     id<CAMetalDrawable> mtlbackbuffer = data->mtlbackbuffer;
   714     id<CAMetalDrawable> mtlbackbuffer = data.mtlbackbuffer;
   708 
   715 
   709     [data->mtlcmdencoder endEncoding];
   716     [data.mtlcmdencoder endEncoding];
   710     [data->mtlcmdbuffer presentDrawable:mtlbackbuffer];
   717     [data.mtlcmdbuffer presentDrawable:mtlbackbuffer];
   711 
   718 
   712     [data->mtlcmdbuffer addCompletedHandler:^(id <MTLCommandBuffer> mtlcmdbuffer){
   719     [data.mtlcmdbuffer addCompletedHandler:^(id <MTLCommandBuffer> mtlcmdbuffer) {
       
   720 #if !__has_feature(objc_arc)
   713         [mtlbackbuffer release];
   721         [mtlbackbuffer release];
       
   722 #endif
   714     }];
   723     }];
   715 
   724 
   716     [data->mtlcmdbuffer commit];
   725     [data.mtlcmdbuffer commit];
   717 
   726 
   718     // Start next frame, once we can.
   727     // Start next frame, once we can.
   719     // we don't specify a depth or stencil buffer because the render API doesn't currently use them.
   728     // we don't specify a depth or stencil buffer because the render API doesn't currently use them.
   720     data->mtlbackbuffer = [data->mtllayer nextDrawable];
   729     data.mtlbackbuffer = [data.mtllayer nextDrawable];
   721     SDL_assert(data->mtlbackbuffer);
   730     SDL_assert(data.mtlbackbuffer);
   722     colorAttachment.texture = data->mtlbackbuffer.texture;
   731     colorAttachment.texture = data.mtlbackbuffer.texture;
   723     colorAttachment.loadAction = MTLLoadActionDontCare;
   732     colorAttachment.loadAction = MTLLoadActionDontCare;
   724     data->mtlcmdbuffer = [data->mtlcmdqueue commandBuffer];
   733     data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
   725     data->mtlcmdencoder = [data->mtlcmdbuffer renderCommandEncoderWithDescriptor:data->mtlpassdesc];
   734     data.mtlcmdencoder = [data.mtlcmdbuffer renderCommandEncoderWithDescriptor:data.mtlpassdesc];
   726     data->mtlcmdencoder.label = @"SDL metal renderer frame";
   735     data.mtlcmdencoder.label = @"SDL metal renderer frame";
   727 
   736 
   728     // Set up our current renderer state for the next frame...
   737     // Set up our current renderer state for the next frame...
   729     METAL_UpdateViewport(renderer);
   738     METAL_UpdateViewport(renderer);
   730     METAL_UpdateClipRect(renderer);
   739     METAL_UpdateClipRect(renderer);
   731 }
   740 }
   732 
   741 
   733 static void
   742 static void
   734 METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   743 METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
   735 {
   744 {
   736     id<MTLTexture> mtltexture = (id<MTLTexture>) texture->driverdata;
   745     id<MTLTexture> mtltexture = CFBridgingRelease(texture->driverdata);
       
   746 #if !__has_feature(objc_arc)
   737     [mtltexture release];
   747     [mtltexture release];
       
   748 #endif
   738     texture->driverdata = NULL;
   749     texture->driverdata = NULL;
   739 }
   750 }
   740 
   751 
   741 static void
   752 static void
   742 METAL_DestroyRenderer(SDL_Renderer * renderer)
   753 METAL_DestroyRenderer(SDL_Renderer * renderer)
   743 {
   754 {
   744     METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
   755     if (renderer->driverdata) {
   745 
   756         METAL_RenderData *data = CFBridgingRelease(renderer->driverdata);
   746     if (data) {
   757 
       
   758 #if !__has_feature(objc_arc)
   747         int i;
   759         int i;
   748         [data->mtlcmdencoder endEncoding];
   760         [data.mtlcmdencoder endEncoding];
   749         [data->mtlcmdencoder release];
   761         [data.mtlcmdencoder release];
   750         [data->mtlcmdbuffer release];
   762         [data.mtlcmdbuffer release];
   751         [data->mtlcmdqueue release];
   763         [data.mtlcmdqueue release];
   752         for (i = 0; i < 4; i++) {
   764         for (i = 0; i < 4; i++) {
   753             [data->mtlpipelineprims[i] release];
   765             [data.mtlpipelineprims[i] release];
   754             [data->mtlpipelinecopy[i] release];
   766             [data.mtlpipelinecopy[i] release];
   755         }
   767         }
   756         [data->mtlbufclearverts release];
   768         [data.mtlbufclearverts release];
   757         [data->mtllibrary release];
   769         [data.mtllibrary release];
   758         [data->mtldevice release];
   770         [data.mtldevice release];
   759         [data->mtlpassdesc release];
   771         [data.mtlpassdesc release];
   760         [data->mtllayer release];
   772         [data.mtllayer release];
   761         SDL_free(data);
   773         [data release];
       
   774 #endif
   762     }
   775     }
   763     SDL_free(renderer);
   776     SDL_free(renderer);
   764 }
   777 }
   765 
   778 
   766 #endif /* SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED */
   779 #endif /* SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED */