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