src/render/metal/SDL_render_metal.m
author Sam Lantinga <slouken@libsdl.org>
Thu, 07 Dec 2017 18:08:51 -0800
changeset 11734 6d6dc7d2a704
parent 11733 490588c02a65
child 11735 1d81fcb6edd5
permissions -rw-r--r--
Fixed runtime errors on iOS
icculus@11729
     1
/*
icculus@11729
     2
  Simple DirectMedia Layer
slouken@11730
     3
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
icculus@11729
     4
icculus@11729
     5
  This software is provided 'as-is', without any express or implied
icculus@11729
     6
  warranty.  In no event will the authors be held liable for any damages
icculus@11729
     7
  arising from the use of this software.
icculus@11729
     8
icculus@11729
     9
  Permission is granted to anyone to use this software for any purpose,
icculus@11729
    10
  including commercial applications, and to alter it and redistribute it
icculus@11729
    11
  freely, subject to the following restrictions:
icculus@11729
    12
icculus@11729
    13
  1. The origin of this software must not be misrepresented; you must not
icculus@11729
    14
     claim that you wrote the original software. If you use this software
icculus@11729
    15
     in a product, an acknowledgment in the product documentation would be
icculus@11729
    16
     appreciated but is not required.
icculus@11729
    17
  2. Altered source versions must be plainly marked as such, and must not be
icculus@11729
    18
     misrepresented as being the original software.
icculus@11729
    19
  3. This notice may not be removed or altered from any source distribution.
icculus@11729
    20
*/
icculus@11729
    21
#include "../../SDL_internal.h"
icculus@11729
    22
icculus@11729
    23
#if SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED
icculus@11729
    24
icculus@11729
    25
#include "SDL_hints.h"
icculus@11729
    26
#include "SDL_log.h"
icculus@11729
    27
#include "SDL_assert.h"
icculus@11729
    28
#include "SDL_syswm.h"
icculus@11729
    29
#include "../SDL_sysrender.h"
icculus@11729
    30
slouken@11732
    31
#ifdef __MACOSX__
icculus@11729
    32
#include <Cocoa/Cocoa.h>
slouken@11732
    33
#else
slouken@11732
    34
#include "../../video/uikit/SDL_uikitmetalview.h"
slouken@11732
    35
#endif
icculus@11729
    36
#include <Metal/Metal.h>
icculus@11729
    37
#include <QuartzCore/CAMetalLayer.h>
icculus@11729
    38
slouken@11730
    39
/* Regenerate these with build-metal-shaders.sh */
slouken@11730
    40
#ifdef __MACOSX__
slouken@11730
    41
#include "SDL_shaders_metal_osx.h"
slouken@11730
    42
#else
slouken@11730
    43
#include "SDL_shaders_metal_ios.h"
slouken@11730
    44
#endif
icculus@11729
    45
icculus@11729
    46
/* Apple Metal renderer implementation */
icculus@11729
    47
icculus@11729
    48
static SDL_Renderer *METAL_CreateRenderer(SDL_Window * window, Uint32 flags);
icculus@11729
    49
static void METAL_WindowEvent(SDL_Renderer * renderer,
icculus@11729
    50
                           const SDL_WindowEvent *event);
icculus@11729
    51
static int METAL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h);
icculus@11729
    52
static int METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
icculus@11729
    53
static int METAL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    54
                            const SDL_Rect * rect, const void *pixels,
icculus@11729
    55
                            int pitch);
icculus@11729
    56
static int METAL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    57
                               const SDL_Rect * rect,
icculus@11729
    58
                               const Uint8 *Yplane, int Ypitch,
icculus@11729
    59
                               const Uint8 *Uplane, int Upitch,
icculus@11729
    60
                               const Uint8 *Vplane, int Vpitch);
icculus@11729
    61
static int METAL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    62
                          const SDL_Rect * rect, void **pixels, int *pitch);
icculus@11729
    63
static void METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
icculus@11729
    64
static int METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
icculus@11729
    65
static int METAL_UpdateViewport(SDL_Renderer * renderer);
icculus@11729
    66
static int METAL_UpdateClipRect(SDL_Renderer * renderer);
icculus@11729
    67
static int METAL_RenderClear(SDL_Renderer * renderer);
icculus@11729
    68
static int METAL_RenderDrawPoints(SDL_Renderer * renderer,
icculus@11729
    69
                               const SDL_FPoint * points, int count);
icculus@11729
    70
static int METAL_RenderDrawLines(SDL_Renderer * renderer,
icculus@11729
    71
                              const SDL_FPoint * points, int count);
icculus@11729
    72
static int METAL_RenderFillRects(SDL_Renderer * renderer,
icculus@11729
    73
                              const SDL_FRect * rects, int count);
icculus@11729
    74
static int METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    75
                         const SDL_Rect * srcrect, const SDL_FRect * dstrect);
icculus@11729
    76
static int METAL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    77
                         const SDL_Rect * srcrect, const SDL_FRect * dstrect,
icculus@11729
    78
                         const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
icculus@11729
    79
static int METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
icculus@11729
    80
                               Uint32 pixel_format, void * pixels, int pitch);
icculus@11729
    81
static void METAL_RenderPresent(SDL_Renderer * renderer);
icculus@11729
    82
static void METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture);
icculus@11729
    83
static void METAL_DestroyRenderer(SDL_Renderer * renderer);
icculus@11729
    84
icculus@11729
    85
SDL_RenderDriver METAL_RenderDriver = {
icculus@11729
    86
    METAL_CreateRenderer,
icculus@11729
    87
    {
icculus@11729
    88
     "metal",
icculus@11729
    89
     (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE),
icculus@11729
    90
     2,
icculus@11729
    91
     {SDL_PIXELFORMAT_ARGB8888, SDL_PIXELFORMAT_ABGR8888},
icculus@11729
    92
     4096,  // !!! FIXME: how do you query Metal for this?
icculus@11729
    93
     4096}
icculus@11729
    94
};
icculus@11729
    95
slouken@11732
    96
@interface METAL_RenderData : NSObject
slouken@11732
    97
    @property (atomic, retain) id<MTLDevice> mtldevice;
slouken@11732
    98
    @property (atomic, retain) id<MTLCommandQueue> mtlcmdqueue;
slouken@11732
    99
    @property (atomic, retain) id<MTLCommandBuffer> mtlcmdbuffer;
slouken@11732
   100
    @property (atomic, retain) id<MTLRenderCommandEncoder> mtlcmdencoder;
slouken@11732
   101
    @property (atomic, retain) id<MTLLibrary> mtllibrary;
slouken@11732
   102
    @property (atomic, retain) id<CAMetalDrawable> mtlbackbuffer;
slouken@11732
   103
    @property (atomic, retain) NSMutableArray *mtlpipelineprims;
slouken@11732
   104
    @property (atomic, retain) NSMutableArray *mtlpipelinecopy;
slouken@11732
   105
    @property (atomic, retain) id<MTLBuffer> mtlbufclearverts;
slouken@11732
   106
    @property (atomic, retain) CAMetalLayer *mtllayer;
slouken@11732
   107
    @property (atomic, retain) MTLRenderPassDescriptor *mtlpassdesc;
slouken@11732
   108
@end
icculus@11729
   109
slouken@11732
   110
@implementation METAL_RenderData
slouken@11732
   111
@end
icculus@11729
   112
icculus@11729
   113
static int
icculus@11729
   114
IsMetalAvailable(const SDL_SysWMinfo *syswm)
icculus@11729
   115
{
slouken@11733
   116
    if (syswm->subsystem != SDL_SYSWM_COCOA && syswm->subsystem != SDL_SYSWM_UIKIT) {
slouken@11733
   117
        return SDL_SetError("Metal render target only supports Cocoa and UIKit video targets at the moment.");
icculus@11729
   118
    }
icculus@11729
   119
icculus@11729
   120
    // this checks a weak symbol.
slouken@11733
   121
#if defined(__MACOSX__)
icculus@11729
   122
    if (MTLCreateSystemDefaultDevice == NULL) {  // probably on 10.10 or lower.
icculus@11729
   123
        return SDL_SetError("Metal framework not available on this system");
icculus@11729
   124
    }
icculus@11729
   125
#endif
icculus@11729
   126
icculus@11729
   127
    return 0;
icculus@11729
   128
}
icculus@11729
   129
icculus@11729
   130
static id<MTLRenderPipelineState>
icculus@11729
   131
MakePipelineState(METAL_RenderData *data, NSString *label, NSString *vertfn,
icculus@11729
   132
                  NSString *fragfn, const SDL_BlendMode blendmode)
icculus@11729
   133
{
slouken@11732
   134
    id<MTLFunction> mtlvertfn = [data.mtllibrary newFunctionWithName:vertfn];
slouken@11732
   135
    id<MTLFunction> mtlfragfn = [data.mtllibrary newFunctionWithName:fragfn];
icculus@11729
   136
    SDL_assert(mtlvertfn != nil);
icculus@11729
   137
    SDL_assert(mtlfragfn != nil);
icculus@11729
   138
icculus@11729
   139
    MTLRenderPipelineDescriptor *mtlpipedesc = [[MTLRenderPipelineDescriptor alloc] init];
icculus@11729
   140
    mtlpipedesc.vertexFunction = mtlvertfn;
icculus@11729
   141
    mtlpipedesc.fragmentFunction = mtlfragfn;
slouken@11732
   142
    mtlpipedesc.colorAttachments[0].pixelFormat = data.mtlbackbuffer.texture.pixelFormat;
icculus@11729
   143
icculus@11729
   144
    switch (blendmode) {
icculus@11729
   145
        case SDL_BLENDMODE_BLEND:
icculus@11729
   146
            mtlpipedesc.colorAttachments[0].blendingEnabled = YES;
icculus@11729
   147
            mtlpipedesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
icculus@11729
   148
            mtlpipedesc.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
icculus@11729
   149
            mtlpipedesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
icculus@11729
   150
            mtlpipedesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
icculus@11729
   151
            mtlpipedesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne;
icculus@11729
   152
            mtlpipedesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
icculus@11729
   153
            break;
icculus@11729
   154
icculus@11729
   155
        case SDL_BLENDMODE_ADD:
icculus@11729
   156
            mtlpipedesc.colorAttachments[0].blendingEnabled = YES;
icculus@11729
   157
            mtlpipedesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
icculus@11729
   158
            mtlpipedesc.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
icculus@11729
   159
            mtlpipedesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
icculus@11729
   160
            mtlpipedesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOne;
icculus@11729
   161
            mtlpipedesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorZero;
icculus@11729
   162
            mtlpipedesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOne;
icculus@11729
   163
            break;
icculus@11729
   164
icculus@11729
   165
        case SDL_BLENDMODE_MOD:
icculus@11729
   166
            mtlpipedesc.colorAttachments[0].blendingEnabled = YES;
icculus@11729
   167
            mtlpipedesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
icculus@11729
   168
            mtlpipedesc.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
icculus@11729
   169
            mtlpipedesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorZero;
icculus@11729
   170
            mtlpipedesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorSourceColor;
icculus@11729
   171
            mtlpipedesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorZero;
icculus@11729
   172
            mtlpipedesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOne;
icculus@11729
   173
            break;
slouken@11730
   174
slouken@11730
   175
        default:
slouken@11730
   176
            mtlpipedesc.colorAttachments[0].blendingEnabled = NO;
slouken@11730
   177
            break;
icculus@11729
   178
    }
icculus@11729
   179
icculus@11729
   180
    mtlpipedesc.label = label;
icculus@11729
   181
icculus@11729
   182
    NSError *err = nil;
slouken@11732
   183
    id<MTLRenderPipelineState> retval = [data.mtldevice newRenderPipelineStateWithDescriptor:mtlpipedesc error:&err];
icculus@11729
   184
    SDL_assert(err == nil);
slouken@11732
   185
#if !__has_feature(objc_arc)
icculus@11729
   186
    [mtlpipedesc release];  // !!! FIXME: can these be reused for each creation, or does the pipeline obtain it?
icculus@11729
   187
    [mtlvertfn release];
icculus@11729
   188
    [mtlfragfn release];
icculus@11729
   189
    [label release];
slouken@11732
   190
#endif
icculus@11729
   191
    return retval;
icculus@11729
   192
}
icculus@11729
   193
icculus@11729
   194
static void
slouken@11732
   195
MakePipelineStates(METAL_RenderData *data, NSMutableArray *states,
icculus@11729
   196
                   NSString *label, NSString *vertfn, NSString *fragfn)
icculus@11729
   197
{
slouken@11732
   198
    [states addObject:MakePipelineState(data, [label stringByAppendingString:@" (blendmode=none)"], vertfn, fragfn, SDL_BLENDMODE_NONE)];
slouken@11732
   199
    [states addObject:MakePipelineState(data, [label stringByAppendingString:@" (blendmode=blend)"], vertfn, fragfn, SDL_BLENDMODE_BLEND)];
slouken@11732
   200
    [states addObject:MakePipelineState(data, [label stringByAppendingString:@" (blendmode=add)"], vertfn, fragfn, SDL_BLENDMODE_ADD)];
slouken@11732
   201
    [states addObject:MakePipelineState(data, [label stringByAppendingString:@" (blendmode=mod)"], vertfn, fragfn, SDL_BLENDMODE_MOD)];
icculus@11729
   202
}
icculus@11729
   203
icculus@11729
   204
static inline id<MTLRenderPipelineState>
slouken@11732
   205
ChoosePipelineState(NSMutableArray *states, const SDL_BlendMode blendmode)
icculus@11729
   206
{
icculus@11729
   207
    switch (blendmode) {
slouken@11732
   208
        case SDL_BLENDMODE_BLEND: return (id<MTLRenderPipelineState>)states[1];
slouken@11732
   209
        case SDL_BLENDMODE_ADD: return (id<MTLRenderPipelineState>)states[2];
slouken@11732
   210
        case SDL_BLENDMODE_MOD: return (id<MTLRenderPipelineState>)states[3];
slouken@11732
   211
        default: return (id<MTLRenderPipelineState>)states[0];
icculus@11729
   212
    }
icculus@11729
   213
    return nil;
icculus@11729
   214
}
icculus@11729
   215
icculus@11729
   216
static SDL_Renderer *
icculus@11729
   217
METAL_CreateRenderer(SDL_Window * window, Uint32 flags)
icculus@11729
   218
{
icculus@11729
   219
    SDL_Renderer *renderer = NULL;
icculus@11729
   220
    METAL_RenderData *data = NULL;
icculus@11729
   221
    SDL_SysWMinfo syswm;
icculus@11729
   222
icculus@11729
   223
    SDL_VERSION(&syswm.version);
icculus@11729
   224
    if (!SDL_GetWindowWMInfo(window, &syswm)) {
icculus@11729
   225
        return NULL;
icculus@11729
   226
    }
icculus@11729
   227
icculus@11729
   228
    if (IsMetalAvailable(&syswm) == -1) {
icculus@11729
   229
        return NULL;
icculus@11729
   230
    }
icculus@11729
   231
slouken@11732
   232
    renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
slouken@11732
   233
    if (!renderer) {
icculus@11729
   234
        SDL_OutOfMemory();
icculus@11729
   235
        return NULL;
icculus@11729
   236
    }
icculus@11729
   237
slouken@11732
   238
    data = [[METAL_RenderData alloc] init];
icculus@11729
   239
slouken@11732
   240
#if __has_feature(objc_arc)
slouken@11732
   241
    renderer->driverdata = (void*)CFBridgingRetain(data);
slouken@11732
   242
#else
icculus@11729
   243
    renderer->driverdata = data;
slouken@11732
   244
#endif
icculus@11729
   245
    renderer->window = window;
icculus@11729
   246
slouken@11730
   247
#ifdef __MACOSX__
slouken@11730
   248
    id<MTLDevice> mtldevice = MTLCreateSystemDefaultDevice();  // !!! FIXME: MTLCopyAllDevices() can find other GPUs...
slouken@11730
   249
    if (mtldevice == nil) {
icculus@11729
   250
        SDL_free(renderer);
slouken@11732
   251
#if !__has_feature(objc_arc)
slouken@11732
   252
        [data release];
slouken@11732
   253
#endif
icculus@11729
   254
        SDL_SetError("Failed to obtain Metal device");
icculus@11729
   255
        return NULL;
icculus@11729
   256
    }
icculus@11729
   257
icculus@11729
   258
    // !!! FIXME: error checking on all of this.
icculus@11729
   259
icculus@11729
   260
    NSView *nsview = [syswm.info.cocoa.window contentView];
icculus@11729
   261
slouken@11730
   262
    // CAMetalLayer is available in QuartzCore starting at OSX 10.11
slouken@11730
   263
    CAMetalLayer *layer = [NSClassFromString( @"CAMetalLayer" ) layer];
icculus@11729
   264
slouken@11730
   265
    layer.device = mtldevice;
icculus@11729
   266
    //layer.pixelFormat = MTLPixelFormatBGRA8Unorm;  // !!! FIXME: MTLPixelFormatBGRA8Unorm_sRGB ?
icculus@11729
   267
    layer.framebufferOnly = YES;
icculus@11729
   268
    //layer.drawableSize = (CGSize) [nsview convertRectToBacking:[nsview bounds]].size;
icculus@11729
   269
    //layer.colorspace = nil;
icculus@11729
   270
icculus@11729
   271
    [nsview setWantsLayer:YES];
icculus@11729
   272
    [nsview setLayer:layer];
icculus@11729
   273
icculus@11729
   274
    [layer retain];
slouken@11730
   275
#else
slouken@11732
   276
    UIView *view = UIKit_Mtl_AddMetalView(window);
slouken@11732
   277
    CAMetalLayer *layer = (CAMetalLayer *)[view layer];
slouken@11730
   278
#endif
slouken@11730
   279
slouken@11732
   280
    data.mtldevice = layer.device;
slouken@11732
   281
    data.mtllayer = layer;
slouken@11732
   282
    data.mtlcmdqueue = [data.mtldevice newCommandQueue];
slouken@11732
   283
    data.mtlcmdqueue.label = @"SDL Metal Renderer";
icculus@11729
   284
slouken@11732
   285
    data.mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor];  // !!! FIXME: is this autoreleased?
icculus@11729
   286
icculus@11729
   287
    // we don't specify a depth or stencil buffer because the render API doesn't currently use them.
slouken@11732
   288
    MTLRenderPassColorAttachmentDescriptor *colorAttachment = data.mtlpassdesc.colorAttachments[0];
slouken@11732
   289
    data.mtlbackbuffer = [data.mtllayer nextDrawable];
slouken@11732
   290
    colorAttachment.texture = data.mtlbackbuffer.texture;
icculus@11729
   291
    colorAttachment.loadAction = MTLLoadActionClear;
icculus@11729
   292
    colorAttachment.clearColor = MTLClearColorMake(0.0f, 0.0f, 0.0f, 1.0f);
slouken@11732
   293
    data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
icculus@11729
   294
icculus@11729
   295
    // Just push a clear to the screen to start so we're in a good state.
slouken@11732
   296
    data.mtlcmdencoder = [data.mtlcmdbuffer renderCommandEncoderWithDescriptor:data.mtlpassdesc];
slouken@11732
   297
    data.mtlcmdencoder.label = @"Initial drawable clear";
icculus@11729
   298
icculus@11729
   299
    METAL_RenderPresent(renderer);
icculus@11729
   300
icculus@11729
   301
    renderer->WindowEvent = METAL_WindowEvent;
icculus@11729
   302
    renderer->GetOutputSize = METAL_GetOutputSize;
icculus@11729
   303
    renderer->CreateTexture = METAL_CreateTexture;
icculus@11729
   304
    renderer->UpdateTexture = METAL_UpdateTexture;
icculus@11729
   305
    renderer->UpdateTextureYUV = METAL_UpdateTextureYUV;
icculus@11729
   306
    renderer->LockTexture = METAL_LockTexture;
icculus@11729
   307
    renderer->UnlockTexture = METAL_UnlockTexture;
icculus@11729
   308
    renderer->SetRenderTarget = METAL_SetRenderTarget;
icculus@11729
   309
    renderer->UpdateViewport = METAL_UpdateViewport;
icculus@11729
   310
    renderer->UpdateClipRect = METAL_UpdateClipRect;
icculus@11729
   311
    renderer->RenderClear = METAL_RenderClear;
icculus@11729
   312
    renderer->RenderDrawPoints = METAL_RenderDrawPoints;
icculus@11729
   313
    renderer->RenderDrawLines = METAL_RenderDrawLines;
icculus@11729
   314
    renderer->RenderFillRects = METAL_RenderFillRects;
icculus@11729
   315
    renderer->RenderCopy = METAL_RenderCopy;
icculus@11729
   316
    renderer->RenderCopyEx = METAL_RenderCopyEx;
icculus@11729
   317
    renderer->RenderReadPixels = METAL_RenderReadPixels;
icculus@11729
   318
    renderer->RenderPresent = METAL_RenderPresent;
icculus@11729
   319
    renderer->DestroyTexture = METAL_DestroyTexture;
icculus@11729
   320
    renderer->DestroyRenderer = METAL_DestroyRenderer;
icculus@11729
   321
icculus@11729
   322
    renderer->info = METAL_RenderDriver.info;
icculus@11729
   323
    renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
icculus@11729
   324
icculus@11729
   325
    // !!! FIXME: how do you control this in Metal?
icculus@11729
   326
    renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
icculus@11729
   327
icculus@11729
   328
    NSError *err = nil;
icculus@11729
   329
slouken@11730
   330
    // The compiled .metallib is embedded in a static array in a header file
slouken@11730
   331
    // but the original shader source code is in SDL_shaders_metal.metal.
icculus@11729
   332
    dispatch_data_t mtllibdata = dispatch_data_create(sdl_metallib, sdl_metallib_len, dispatch_get_global_queue(0, 0), ^{});
slouken@11732
   333
    data.mtllibrary = [data.mtldevice newLibraryWithData:mtllibdata error:&err];
icculus@11729
   334
    SDL_assert(err == nil);
slouken@11732
   335
#if !__has_feature(objc_arc)
icculus@11729
   336
    dispatch_release(mtllibdata);
slouken@11732
   337
#endif
slouken@11732
   338
    data.mtllibrary.label = @"SDL Metal renderer shader library";
icculus@11729
   339
slouken@11732
   340
    data.mtlpipelineprims = [[NSMutableArray alloc] init];
slouken@11732
   341
    MakePipelineStates(data, data.mtlpipelineprims, @"SDL primitives pipeline", @"SDL_Simple_vertex", @"SDL_Simple_fragment");
slouken@11732
   342
    data.mtlpipelinecopy = [[NSMutableArray alloc] init];
slouken@11732
   343
    MakePipelineStates(data, data.mtlpipelinecopy, @"SDL_RenderCopy pipeline", @"SDL_Copy_vertex", @"SDL_Copy_fragment");
icculus@11729
   344
icculus@11729
   345
    static const float clearverts[] = { -1, -1, -1, 1, 1, 1, 1, -1, -1, -1 };
slouken@11734
   346
    data.mtlbufclearverts = [data.mtldevice newBufferWithBytes:clearverts length:sizeof(clearverts) options:MTLResourceCPUCacheModeWriteCombined];
slouken@11732
   347
    data.mtlbufclearverts.label = @"SDL_RenderClear vertices";
icculus@11729
   348
icculus@11729
   349
    // !!! FIXME: force more clears here so all the drawables are sane to start, and our static buffers are definitely flushed.
icculus@11729
   350
icculus@11729
   351
    return renderer;
icculus@11729
   352
}
icculus@11729
   353
icculus@11729
   354
static void
icculus@11729
   355
METAL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
icculus@11729
   356
{
icculus@11729
   357
    if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
icculus@11729
   358
        event->event == SDL_WINDOWEVENT_SHOWN ||
icculus@11729
   359
        event->event == SDL_WINDOWEVENT_HIDDEN) {
icculus@11729
   360
        // !!! FIXME: write me
icculus@11729
   361
    }
icculus@11729
   362
}
icculus@11729
   363
icculus@11729
   364
static int
icculus@11729
   365
METAL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
icculus@11729
   366
{
slouken@11732
   367
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slouken@11732
   368
    *w = (int) data.mtlbackbuffer.texture.width;
slouken@11732
   369
    *h = (int) data.mtlbackbuffer.texture.height;
icculus@11729
   370
    return 0;
icculus@11729
   371
}
icculus@11729
   372
icculus@11729
   373
static int
icculus@11729
   374
METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
icculus@11729
   375
{
slouken@11732
   376
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@11729
   377
    MTLPixelFormat mtlpixfmt;
icculus@11729
   378
icculus@11729
   379
    switch (texture->format) {
icculus@11729
   380
        case SDL_PIXELFORMAT_ABGR8888: mtlpixfmt = MTLPixelFormatRGBA8Unorm; break;
icculus@11729
   381
        case SDL_PIXELFORMAT_ARGB8888: mtlpixfmt = MTLPixelFormatBGRA8Unorm; break;
icculus@11729
   382
        default: return SDL_SetError("Texture format %s not supported by Metal", SDL_GetPixelFormatName(texture->format));
icculus@11729
   383
    }
icculus@11729
   384
icculus@11729
   385
    // !!! FIXME: autorelease or nah?
icculus@11729
   386
    MTLTextureDescriptor *mtltexdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:mtlpixfmt
icculus@11729
   387
                                            width:(NSUInteger)texture->w height:(NSUInteger)texture->h mipmapped:NO];
icculus@11729
   388
slouken@11732
   389
    id<MTLTexture> mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
slouken@11732
   390
#if !__has_feature(objc_arc)
icculus@11729
   391
    [mtltexdesc release];
slouken@11732
   392
#endif
icculus@11729
   393
    if (mtltexture == nil) {
icculus@11729
   394
        return SDL_SetError("Texture allocation failed");
icculus@11729
   395
    }
icculus@11729
   396
slouken@11732
   397
    texture->driverdata = (void*)CFBridgingRetain(mtltexture);
icculus@11729
   398
icculus@11729
   399
    return 0;
icculus@11729
   400
}
icculus@11729
   401
icculus@11729
   402
static int
icculus@11729
   403
METAL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   404
                 const SDL_Rect * rect, const void *pixels, int pitch)
icculus@11729
   405
{
icculus@11729
   406
    // !!! FIXME: this is a synchronous call; it doesn't return until data is uploaded in some form.
icculus@11729
   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
icculus@11729
   408
    // !!! FIXME:  use this texture before the marking is done? Is it worth it? Or will we basically always be uploading a bunch of
icculus@11729
   409
    // !!! FIXME:  stuff way ahead of time and/or using it immediately after upload?
slouken@11732
   410
    id<MTLTexture> mtltexture = (__bridge id<MTLTexture>) texture->driverdata;
icculus@11729
   411
    [mtltexture replaceRegion:MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h) mipmapLevel:0 withBytes:pixels bytesPerRow:pitch];
icculus@11729
   412
    return 0;
icculus@11729
   413
}
icculus@11729
   414
icculus@11729
   415
static int
icculus@11729
   416
METAL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   417
                    const SDL_Rect * rect,
icculus@11729
   418
                    const Uint8 *Yplane, int Ypitch,
icculus@11729
   419
                    const Uint8 *Uplane, int Upitch,
icculus@11729
   420
                    const Uint8 *Vplane, int Vpitch)
icculus@11729
   421
{
icculus@11729
   422
    return SDL_Unsupported();  // !!! FIXME
icculus@11729
   423
}
icculus@11729
   424
icculus@11729
   425
static int
icculus@11729
   426
METAL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   427
               const SDL_Rect * rect, void **pixels, int *pitch)
icculus@11729
   428
{
icculus@11729
   429
    return SDL_Unsupported();   // !!! FIXME: write me
icculus@11729
   430
}
icculus@11729
   431
icculus@11729
   432
static void
icculus@11729
   433
METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
icculus@11729
   434
{
icculus@11729
   435
    // !!! FIXME: write me
icculus@11729
   436
}
icculus@11729
   437
icculus@11729
   438
static int
icculus@11729
   439
METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
icculus@11729
   440
{
slouken@11732
   441
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slouken@11732
   442
    id<MTLTexture> mtltexture = texture ? (__bridge id<MTLTexture>) texture->driverdata : nil;
slouken@11732
   443
    data.mtlpassdesc.colorAttachments[0].texture = mtltexture;
icculus@11729
   444
    return 0;
icculus@11729
   445
}
icculus@11729
   446
icculus@11729
   447
static int
icculus@11729
   448
METAL_UpdateViewport(SDL_Renderer * renderer)
icculus@11729
   449
{
slouken@11732
   450
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slouken@11732
   451
    if (data.mtlcmdencoder != nil) {
icculus@11729
   452
        MTLViewport viewport;
icculus@11729
   453
        viewport.originX = renderer->viewport.x;
icculus@11729
   454
        viewport.originY = renderer->viewport.y;
icculus@11729
   455
        viewport.width = renderer->viewport.w;
icculus@11729
   456
        viewport.height = renderer->viewport.h;
icculus@11729
   457
        viewport.znear = 0.0;
icculus@11729
   458
        viewport.zfar = 1.0;
slouken@11732
   459
        [data.mtlcmdencoder setViewport:viewport];
icculus@11729
   460
    }
icculus@11729
   461
    return 0;
icculus@11729
   462
}
icculus@11729
   463
icculus@11729
   464
static int
icculus@11729
   465
METAL_UpdateClipRect(SDL_Renderer * renderer)
icculus@11729
   466
{
icculus@11729
   467
    // !!! FIXME: should this care about the viewport?
slouken@11732
   468
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slouken@11732
   469
    if (data.mtlcmdencoder != nil) {
icculus@11729
   470
        MTLScissorRect mtlrect;
icculus@11729
   471
        if (renderer->clipping_enabled) {
icculus@11729
   472
            const SDL_Rect *rect = &renderer->clip_rect;
icculus@11729
   473
            mtlrect.x = renderer->viewport.x + rect->x;
icculus@11729
   474
            mtlrect.y = renderer->viewport.x + rect->y;
icculus@11729
   475
            mtlrect.width = rect->w;
icculus@11729
   476
            mtlrect.height = rect->h;
icculus@11729
   477
        } else {
icculus@11729
   478
            mtlrect.x = renderer->viewport.x;
icculus@11729
   479
            mtlrect.y = renderer->viewport.y;
icculus@11729
   480
            mtlrect.width = renderer->viewport.w;
icculus@11729
   481
            mtlrect.height = renderer->viewport.h;
icculus@11729
   482
        }
slouken@11734
   483
        if (mtlrect.width > 0 && mtlrect.height > 0) {
slouken@11734
   484
            [data.mtlcmdencoder setScissorRect:mtlrect];
slouken@11734
   485
        }
icculus@11729
   486
    }
icculus@11729
   487
    return 0;
icculus@11729
   488
}
icculus@11729
   489
icculus@11729
   490
static int
icculus@11729
   491
METAL_RenderClear(SDL_Renderer * renderer)
icculus@11729
   492
{
icculus@11729
   493
    // We could dump the command buffer and force a clear on a new one, but this will respect the scissor state.
slouken@11732
   494
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@11729
   495
icculus@11729
   496
    // !!! FIXME: render color should live in a dedicated uniform buffer.
icculus@11729
   497
    const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
icculus@11729
   498
icculus@11729
   499
    MTLViewport viewport;  // RenderClear ignores the viewport state, though, so reset that.
icculus@11729
   500
    viewport.originX = viewport.originY = 0.0;
slouken@11732
   501
    viewport.width = data.mtlbackbuffer.texture.width;
slouken@11732
   502
    viewport.height = data.mtlbackbuffer.texture.height;
icculus@11729
   503
    viewport.znear = 0.0;
icculus@11729
   504
    viewport.zfar = 1.0;
icculus@11729
   505
icculus@11729
   506
    // Draw as if we're doing a simple filled rect to the screen now.
slouken@11732
   507
    [data.mtlcmdencoder setViewport:viewport];
slouken@11732
   508
    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
slouken@11732
   509
    [data.mtlcmdencoder setVertexBuffer:data.mtlbufclearverts offset:0 atIndex:0];
slouken@11732
   510
    [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
slouken@11732
   511
    [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:5];
icculus@11729
   512
icculus@11729
   513
    // reset the viewport for the rest of our usual drawing work...
icculus@11729
   514
    viewport.originX = renderer->viewport.x;
icculus@11729
   515
    viewport.originY = renderer->viewport.y;
icculus@11729
   516
    viewport.width = renderer->viewport.w;
icculus@11729
   517
    viewport.height = renderer->viewport.h;
icculus@11729
   518
    viewport.znear = 0.0;
icculus@11729
   519
    viewport.zfar = 1.0;
slouken@11732
   520
    [data.mtlcmdencoder setViewport:viewport];
icculus@11729
   521
icculus@11729
   522
    return 0;
icculus@11729
   523
}
icculus@11729
   524
icculus@11729
   525
// normalize a value from 0.0f to len into -1.0f to 1.0f.
icculus@11729
   526
static inline float
icculus@11729
   527
norm(const float _val, const float len)
icculus@11729
   528
{
icculus@11729
   529
    const float val = (_val < 0.0f) ? 0.0f : (_val > len) ? len : _val;
icculus@11729
   530
    return ((val / len) * 2.0f) - 1.0f;  // !!! FIXME: is this right?
icculus@11729
   531
}
icculus@11729
   532
icculus@11729
   533
// normalize a value from 0.0f to len into -1.0f to 1.0f.
icculus@11729
   534
static inline float
icculus@11729
   535
normy(const float _val, const float len)
icculus@11729
   536
{
icculus@11729
   537
    return norm(len - ((_val < 0.0f) ? 0.0f : (_val > len) ? len : _val), len);
icculus@11729
   538
}
icculus@11729
   539
icculus@11729
   540
// normalize a value from 0.0f to len into 0.0f to 1.0f.
icculus@11729
   541
static inline float
icculus@11729
   542
normtex(const float _val, const float len)
icculus@11729
   543
{
icculus@11729
   544
    const float val = (_val < 0.0f) ? 0.0f : (_val > len) ? len : _val;
icculus@11729
   545
    return (val / len);
icculus@11729
   546
}
icculus@11729
   547
icculus@11729
   548
static int
icculus@11729
   549
DrawVerts(SDL_Renderer * renderer, const SDL_FPoint * points, int count,
icculus@11729
   550
          const MTLPrimitiveType primtype)
icculus@11729
   551
{
icculus@11729
   552
    const size_t vertlen = (sizeof (float) * 2) * count;
icculus@11729
   553
    float *verts = SDL_malloc(vertlen);
icculus@11729
   554
    if (!verts) {
icculus@11729
   555
        return SDL_OutOfMemory();
icculus@11729
   556
    }
icculus@11729
   557
slouken@11732
   558
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@11729
   559
icculus@11729
   560
    // !!! FIXME: render color should live in a dedicated uniform buffer.
icculus@11729
   561
    const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
icculus@11729
   562
slouken@11732
   563
    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
slouken@11732
   564
    [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
icculus@11729
   565
slouken@11732
   566
    const float w = (float) data.mtlpassdesc.colorAttachments[0].texture.width;
slouken@11732
   567
    const float h = (float) data.mtlpassdesc.colorAttachments[0].texture.height;
icculus@11729
   568
icculus@11729
   569
    // !!! FIXME: we can convert this in the shader. This will save the malloc and for-loop, but we still need to upload.
icculus@11729
   570
    float *ptr = verts;
icculus@11729
   571
    for (int i = 0; i < count; i++, points++) {
icculus@11729
   572
        *ptr = norm(points->x, w); ptr++;
icculus@11729
   573
        *ptr = normy(points->y, h); ptr++;
icculus@11729
   574
    }
icculus@11729
   575
slouken@11732
   576
    [data.mtlcmdencoder setVertexBytes:verts length:vertlen atIndex:0];
slouken@11732
   577
    [data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];
icculus@11729
   578
icculus@11729
   579
    SDL_free(verts);
icculus@11729
   580
    return 0;
icculus@11729
   581
}
icculus@11729
   582
icculus@11729
   583
static int
icculus@11729
   584
METAL_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points, int count)
icculus@11729
   585
{
icculus@11729
   586
    return DrawVerts(renderer, points, count, MTLPrimitiveTypePoint);
icculus@11729
   587
}
icculus@11729
   588
icculus@11729
   589
static int
icculus@11729
   590
METAL_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points, int count)
icculus@11729
   591
{
icculus@11729
   592
    return DrawVerts(renderer, points, count, MTLPrimitiveTypeLineStrip);
icculus@11729
   593
}
icculus@11729
   594
icculus@11729
   595
static int
icculus@11729
   596
METAL_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count)
icculus@11729
   597
{
slouken@11732
   598
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@11729
   599
icculus@11729
   600
    // !!! FIXME: render color should live in a dedicated uniform buffer.
icculus@11729
   601
    const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
icculus@11729
   602
slouken@11732
   603
    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
slouken@11732
   604
    [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
icculus@11729
   605
slouken@11732
   606
    const float w = (float) data.mtlpassdesc.colorAttachments[0].texture.width;
slouken@11732
   607
    const float h = (float) data.mtlpassdesc.colorAttachments[0].texture.height;
icculus@11729
   608
icculus@11729
   609
    for (int i = 0; i < count; i++, rects++) {
icculus@11729
   610
        if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) continue;
icculus@11729
   611
icculus@11729
   612
        const float verts[] = {
icculus@11729
   613
            norm(rects->x, w), normy(rects->y + rects->h, h),
icculus@11729
   614
            norm(rects->x, w), normy(rects->y, h),
icculus@11729
   615
            norm(rects->x + rects->w, w), normy(rects->y, h),
icculus@11729
   616
            norm(rects->x, w), normy(rects->y + rects->h, h),
icculus@11729
   617
            norm(rects->x + rects->w, w), normy(rects->y + rects->h, h)
icculus@11729
   618
        };
icculus@11729
   619
slouken@11732
   620
        [data.mtlcmdencoder setVertexBytes:verts length:sizeof(verts) atIndex:0];
slouken@11732
   621
        [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:5];
icculus@11729
   622
    }
icculus@11729
   623
icculus@11729
   624
    return 0;
icculus@11729
   625
}
icculus@11729
   626
icculus@11729
   627
static int
icculus@11729
   628
METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   629
              const SDL_Rect * srcrect, const SDL_FRect * dstrect)
icculus@11729
   630
{
slouken@11732
   631
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slouken@11732
   632
    id<MTLTexture> mtltexture = (__bridge id<MTLTexture>) texture->driverdata;
slouken@11732
   633
    const float w = (float) data.mtlpassdesc.colorAttachments[0].texture.width;
slouken@11732
   634
    const float h = (float) data.mtlpassdesc.colorAttachments[0].texture.height;
icculus@11729
   635
    const float texw = (float) mtltexture.width;
icculus@11729
   636
    const float texh = (float) mtltexture.height;
icculus@11729
   637
icculus@11729
   638
    const float xy[] = {
icculus@11729
   639
        norm(dstrect->x, w), normy(dstrect->y + dstrect->h, h),
icculus@11729
   640
        norm(dstrect->x, w), normy(dstrect->y, h),
icculus@11729
   641
        norm(dstrect->x + dstrect->w, w), normy(dstrect->y, h),
icculus@11729
   642
        norm(dstrect->x, w), normy(dstrect->y + dstrect->h, h),
icculus@11729
   643
        norm(dstrect->x + dstrect->w, w), normy(dstrect->y + dstrect->h, h)
icculus@11729
   644
    };
icculus@11729
   645
icculus@11729
   646
    const float uv[] = {
icculus@11729
   647
        normtex(srcrect->x, texw), normtex(srcrect->y + srcrect->h, texh),
icculus@11729
   648
        normtex(srcrect->x, texw), normtex(srcrect->y, texh),
icculus@11729
   649
        normtex(srcrect->x + srcrect->w, texw), normtex(srcrect->y, texh),
icculus@11729
   650
        normtex(srcrect->x, texw), normtex(srcrect->y + srcrect->h, texh),
icculus@11729
   651
        normtex(srcrect->x + srcrect->w, texw), normtex(srcrect->y + srcrect->h, texh)
icculus@11729
   652
    };
icculus@11729
   653
icculus@11729
   654
    float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
icculus@11729
   655
    if (texture->modMode) {
icculus@11729
   656
        color[0] = ((float)texture->r) / 255.0f;
icculus@11729
   657
        color[1] = ((float)texture->g) / 255.0f;
icculus@11729
   658
        color[2] = ((float)texture->b) / 255.0f;
icculus@11729
   659
        color[3] = ((float)texture->a) / 255.0f;
icculus@11729
   660
    }
icculus@11729
   661
slouken@11732
   662
    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelinecopy, texture->blendMode)];
slouken@11732
   663
    [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
slouken@11732
   664
    [data.mtlcmdencoder setFragmentTexture:mtltexture atIndex:0];
slouken@11732
   665
    [data.mtlcmdencoder setVertexBytes:xy length:sizeof(xy) atIndex:0];
slouken@11732
   666
    [data.mtlcmdencoder setVertexBytes:uv length:sizeof(uv) atIndex:1];
slouken@11732
   667
    [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:5];
icculus@11729
   668
icculus@11729
   669
    return 0;
icculus@11729
   670
}
icculus@11729
   671
icculus@11729
   672
static int
icculus@11729
   673
METAL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   674
              const SDL_Rect * srcrect, const SDL_FRect * dstrect,
icculus@11729
   675
              const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
icculus@11729
   676
{
icculus@11729
   677
    return SDL_Unsupported();  // !!! FIXME
icculus@11729
   678
}
icculus@11729
   679
icculus@11729
   680
static int
icculus@11729
   681
METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
icculus@11729
   682
                    Uint32 pixel_format, void * pixels, int pitch)
icculus@11729
   683
{
slouken@11732
   684
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slouken@11732
   685
    MTLRenderPassColorAttachmentDescriptor *colorAttachment = data.mtlpassdesc.colorAttachments[0];
icculus@11729
   686
    id<MTLTexture> mtltexture = colorAttachment.texture;
icculus@11729
   687
    MTLRegion mtlregion;
icculus@11729
   688
icculus@11729
   689
    mtlregion.origin.x = rect->x;
icculus@11729
   690
    mtlregion.origin.y = rect->y;
icculus@11729
   691
    mtlregion.origin.z = 0;
icculus@11729
   692
    mtlregion.size.width = rect->w;
icculus@11729
   693
    mtlregion.size.height = rect->w;
icculus@11729
   694
    mtlregion.size.depth = 1;
icculus@11729
   695
icculus@11729
   696
    // we only do BGRA8 or RGBA8 at the moment, so 4 will do.
icculus@11729
   697
    const int temp_pitch = rect->w * 4;
icculus@11729
   698
    void *temp_pixels = SDL_malloc(temp_pitch * rect->h);
icculus@11729
   699
    if (!temp_pixels) {
icculus@11729
   700
        return SDL_OutOfMemory();
icculus@11729
   701
    }
icculus@11729
   702
icculus@11729
   703
    [mtltexture getBytes:temp_pixels bytesPerRow:temp_pitch fromRegion:mtlregion mipmapLevel:0];
icculus@11729
   704
icculus@11729
   705
    const Uint32 temp_format = (mtltexture.pixelFormat == MTLPixelFormatBGRA8Unorm) ? SDL_PIXELFORMAT_ARGB8888 : SDL_PIXELFORMAT_ABGR8888;
icculus@11729
   706
    const int status = SDL_ConvertPixels(rect->w, rect->h, temp_format, temp_pixels, temp_pitch, pixel_format, pixels, pitch);
icculus@11729
   707
    SDL_free(temp_pixels);
icculus@11729
   708
    return status;
icculus@11729
   709
}
icculus@11729
   710
icculus@11729
   711
static void
icculus@11729
   712
METAL_RenderPresent(SDL_Renderer * renderer)
icculus@11729
   713
{
slouken@11732
   714
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slouken@11732
   715
    MTLRenderPassColorAttachmentDescriptor *colorAttachment = data.mtlpassdesc.colorAttachments[0];
slouken@11732
   716
    id<CAMetalDrawable> mtlbackbuffer = data.mtlbackbuffer;
icculus@11729
   717
slouken@11732
   718
    [data.mtlcmdencoder endEncoding];
slouken@11732
   719
    [data.mtlcmdbuffer presentDrawable:mtlbackbuffer];
icculus@11729
   720
slouken@11732
   721
    [data.mtlcmdbuffer addCompletedHandler:^(id <MTLCommandBuffer> mtlcmdbuffer) {
slouken@11732
   722
#if !__has_feature(objc_arc)
icculus@11729
   723
        [mtlbackbuffer release];
slouken@11732
   724
#endif
icculus@11729
   725
    }];
icculus@11729
   726
slouken@11732
   727
    [data.mtlcmdbuffer commit];
icculus@11729
   728
icculus@11729
   729
    // Start next frame, once we can.
icculus@11729
   730
    // we don't specify a depth or stencil buffer because the render API doesn't currently use them.
slouken@11732
   731
    data.mtlbackbuffer = [data.mtllayer nextDrawable];
slouken@11732
   732
    SDL_assert(data.mtlbackbuffer);
slouken@11732
   733
    colorAttachment.texture = data.mtlbackbuffer.texture;
icculus@11729
   734
    colorAttachment.loadAction = MTLLoadActionDontCare;
slouken@11732
   735
    data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
slouken@11732
   736
    data.mtlcmdencoder = [data.mtlcmdbuffer renderCommandEncoderWithDescriptor:data.mtlpassdesc];
slouken@11732
   737
    data.mtlcmdencoder.label = @"SDL metal renderer frame";
icculus@11729
   738
icculus@11729
   739
    // Set up our current renderer state for the next frame...
icculus@11729
   740
    METAL_UpdateViewport(renderer);
icculus@11729
   741
    METAL_UpdateClipRect(renderer);
icculus@11729
   742
}
icculus@11729
   743
icculus@11729
   744
static void
icculus@11729
   745
METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
icculus@11729
   746
{
slouken@11732
   747
    id<MTLTexture> mtltexture = CFBridgingRelease(texture->driverdata);
slouken@11732
   748
#if !__has_feature(objc_arc)
icculus@11729
   749
    [mtltexture release];
slouken@11732
   750
#endif
icculus@11729
   751
    texture->driverdata = NULL;
icculus@11729
   752
}
icculus@11729
   753
icculus@11729
   754
static void
icculus@11729
   755
METAL_DestroyRenderer(SDL_Renderer * renderer)
icculus@11729
   756
{
slouken@11732
   757
    if (renderer->driverdata) {
slouken@11732
   758
        METAL_RenderData *data = CFBridgingRelease(renderer->driverdata);
icculus@11729
   759
slouken@11732
   760
#if !__has_feature(objc_arc)
icculus@11729
   761
        int i;
slouken@11732
   762
        [data.mtlcmdencoder endEncoding];
slouken@11732
   763
        [data.mtlcmdencoder release];
slouken@11732
   764
        [data.mtlcmdbuffer release];
slouken@11732
   765
        [data.mtlcmdqueue release];
icculus@11729
   766
        for (i = 0; i < 4; i++) {
slouken@11732
   767
            [data.mtlpipelineprims[i] release];
slouken@11732
   768
            [data.mtlpipelinecopy[i] release];
icculus@11729
   769
        }
slouken@11732
   770
        [data.mtlbufclearverts release];
slouken@11732
   771
        [data.mtllibrary release];
slouken@11732
   772
        [data.mtldevice release];
slouken@11732
   773
        [data.mtlpassdesc release];
slouken@11732
   774
        [data.mtllayer release];
slouken@11732
   775
        [data release];
slouken@11732
   776
#endif
icculus@11729
   777
    }
icculus@11729
   778
    SDL_free(renderer);
icculus@11729
   779
}
icculus@11729
   780
icculus@11729
   781
#endif /* SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED */
icculus@11729
   782
icculus@11729
   783
/* vi: set ts=4 sw=4 expandtab: */