src/render/metal/SDL_render_metal.m
author Sam Lantinga <slouken@libsdl.org>
Thu, 07 Dec 2017 16:08:09 -0800
changeset 11730 ac6c607e065c
parent 11729 d1ce8396c356
child 11732 ad13456d6e7f
permissions -rw-r--r--
Enable building the Metal renderer by default, and weak link the Metal framework so the SDL library is safe to use on older Macs
Also generate iOS versions of the Metal shaders
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
icculus@11729
    31
#include <Cocoa/Cocoa.h>
icculus@11729
    32
#include <Metal/Metal.h>
icculus@11729
    33
#include <QuartzCore/CAMetalLayer.h>
icculus@11729
    34
slouken@11730
    35
/* Regenerate these with build-metal-shaders.sh */
slouken@11730
    36
#ifdef __MACOSX__
slouken@11730
    37
#include "SDL_shaders_metal_osx.h"
slouken@11730
    38
#else
slouken@11730
    39
#include "SDL_shaders_metal_ios.h"
slouken@11730
    40
#endif
icculus@11729
    41
icculus@11729
    42
/* Apple Metal renderer implementation */
icculus@11729
    43
icculus@11729
    44
static SDL_Renderer *METAL_CreateRenderer(SDL_Window * window, Uint32 flags);
icculus@11729
    45
static void METAL_WindowEvent(SDL_Renderer * renderer,
icculus@11729
    46
                           const SDL_WindowEvent *event);
icculus@11729
    47
static int METAL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h);
icculus@11729
    48
static int METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
icculus@11729
    49
static int METAL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    50
                            const SDL_Rect * rect, const void *pixels,
icculus@11729
    51
                            int pitch);
icculus@11729
    52
static int METAL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    53
                               const SDL_Rect * rect,
icculus@11729
    54
                               const Uint8 *Yplane, int Ypitch,
icculus@11729
    55
                               const Uint8 *Uplane, int Upitch,
icculus@11729
    56
                               const Uint8 *Vplane, int Vpitch);
icculus@11729
    57
static int METAL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    58
                          const SDL_Rect * rect, void **pixels, int *pitch);
icculus@11729
    59
static void METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
icculus@11729
    60
static int METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
icculus@11729
    61
static int METAL_UpdateViewport(SDL_Renderer * renderer);
icculus@11729
    62
static int METAL_UpdateClipRect(SDL_Renderer * renderer);
icculus@11729
    63
static int METAL_RenderClear(SDL_Renderer * renderer);
icculus@11729
    64
static int METAL_RenderDrawPoints(SDL_Renderer * renderer,
icculus@11729
    65
                               const SDL_FPoint * points, int count);
icculus@11729
    66
static int METAL_RenderDrawLines(SDL_Renderer * renderer,
icculus@11729
    67
                              const SDL_FPoint * points, int count);
icculus@11729
    68
static int METAL_RenderFillRects(SDL_Renderer * renderer,
icculus@11729
    69
                              const SDL_FRect * rects, int count);
icculus@11729
    70
static int METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    71
                         const SDL_Rect * srcrect, const SDL_FRect * dstrect);
icculus@11729
    72
static int METAL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    73
                         const SDL_Rect * srcrect, const SDL_FRect * dstrect,
icculus@11729
    74
                         const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
icculus@11729
    75
static int METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
icculus@11729
    76
                               Uint32 pixel_format, void * pixels, int pitch);
icculus@11729
    77
static void METAL_RenderPresent(SDL_Renderer * renderer);
icculus@11729
    78
static void METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture);
icculus@11729
    79
static void METAL_DestroyRenderer(SDL_Renderer * renderer);
icculus@11729
    80
icculus@11729
    81
SDL_RenderDriver METAL_RenderDriver = {
icculus@11729
    82
    METAL_CreateRenderer,
icculus@11729
    83
    {
icculus@11729
    84
     "metal",
icculus@11729
    85
     (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE),
icculus@11729
    86
     2,
icculus@11729
    87
     {SDL_PIXELFORMAT_ARGB8888, SDL_PIXELFORMAT_ABGR8888},
icculus@11729
    88
     4096,  // !!! FIXME: how do you query Metal for this?
icculus@11729
    89
     4096}
icculus@11729
    90
};
icculus@11729
    91
icculus@11729
    92
typedef struct METAL_BufferList
icculus@11729
    93
{
icculus@11729
    94
    id<MTLBuffer> mtlbuffer;
icculus@11729
    95
    struct METAL_BufferPool *next;
icculus@11729
    96
} METAL_BufferList;
icculus@11729
    97
icculus@11729
    98
typedef struct
icculus@11729
    99
{
icculus@11729
   100
    id<MTLDevice> mtldevice;
icculus@11729
   101
    id<MTLCommandQueue> mtlcmdqueue;
icculus@11729
   102
    id<MTLCommandBuffer> mtlcmdbuffer;
icculus@11729
   103
    id<MTLRenderCommandEncoder> mtlcmdencoder;
icculus@11729
   104
    id<MTLLibrary> mtllibrary;
icculus@11729
   105
    id<CAMetalDrawable> mtlbackbuffer;
icculus@11729
   106
    id<MTLRenderPipelineState> mtlpipelineprims[4];
icculus@11729
   107
    id<MTLRenderPipelineState> mtlpipelinecopy[4];
icculus@11729
   108
    id<MTLBuffer> mtlbufclearverts;
icculus@11729
   109
    CAMetalLayer *mtllayer;
icculus@11729
   110
    MTLRenderPassDescriptor *mtlpassdesc;
icculus@11729
   111
} METAL_RenderData;
icculus@11729
   112
icculus@11729
   113
icculus@11729
   114
static int
icculus@11729
   115
IsMetalAvailable(const SDL_SysWMinfo *syswm)
icculus@11729
   116
{
icculus@11729
   117
    if (syswm->subsystem != SDL_SYSWM_COCOA) {  // !!! FIXME: SDL_SYSWM_UIKIT for iOS, too!
icculus@11729
   118
        return SDL_SetError("Metal render target only supports Cocoa video target at the moment.");
icculus@11729
   119
    }
icculus@11729
   120
icculus@11729
   121
    // this checks a weak symbol.
icculus@11729
   122
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101100
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
{
icculus@11729
   135
    id<MTLFunction> mtlvertfn = [data->mtllibrary newFunctionWithName:vertfn];
icculus@11729
   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;
icculus@11729
   143
    mtlpipedesc.colorAttachments[0].pixelFormat = data->mtlbackbuffer.texture.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;
icculus@11729
   184
    id<MTLRenderPipelineState> retval = [data->mtldevice newRenderPipelineStateWithDescriptor:mtlpipedesc error:&err];
icculus@11729
   185
    SDL_assert(err == nil);
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];
icculus@11729
   190
icculus@11729
   191
    return retval;
icculus@11729
   192
}
icculus@11729
   193
icculus@11729
   194
static void
icculus@11729
   195
MakePipelineStates(METAL_RenderData *data, id<MTLRenderPipelineState> *states,
icculus@11729
   196
                   NSString *label, NSString *vertfn, NSString *fragfn)
icculus@11729
   197
{
icculus@11729
   198
    int i = 0;
icculus@11729
   199
    states[i++] = MakePipelineState(data, [label stringByAppendingString:@" (blendmode=none)"], vertfn, fragfn, SDL_BLENDMODE_NONE);
icculus@11729
   200
    states[i++] = MakePipelineState(data, [label stringByAppendingString:@" (blendmode=blend)"], vertfn, fragfn, SDL_BLENDMODE_BLEND);
icculus@11729
   201
    states[i++] = MakePipelineState(data, [label stringByAppendingString:@" (blendmode=add)"], vertfn, fragfn, SDL_BLENDMODE_ADD);
icculus@11729
   202
    states[i++] = MakePipelineState(data, [label stringByAppendingString:@" (blendmode=mod)"], vertfn, fragfn, SDL_BLENDMODE_MOD);
icculus@11729
   203
}
icculus@11729
   204
icculus@11729
   205
static inline id<MTLRenderPipelineState>
icculus@11729
   206
ChoosePipelineState(id<MTLRenderPipelineState> *states, const SDL_BlendMode blendmode)
icculus@11729
   207
{
icculus@11729
   208
    switch (blendmode) {
icculus@11729
   209
        case SDL_BLENDMODE_BLEND: return states[1];
icculus@11729
   210
        case SDL_BLENDMODE_ADD: return states[2];
icculus@11729
   211
        case SDL_BLENDMODE_MOD: return states[3];
slouken@11730
   212
        default: return 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
icculus@11729
   233
    data = (METAL_RenderData *) SDL_calloc(1, sizeof(*data));
icculus@11729
   234
    if (!data) {
icculus@11729
   235
        SDL_OutOfMemory();
icculus@11729
   236
        return NULL;
icculus@11729
   237
    }
icculus@11729
   238
icculus@11729
   239
    renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
icculus@11729
   240
    if (!renderer) {
icculus@11729
   241
        SDL_free(data);
icculus@11729
   242
        SDL_OutOfMemory();
icculus@11729
   243
        return NULL;
icculus@11729
   244
    }
icculus@11729
   245
icculus@11729
   246
    renderer->driverdata = data;
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);
icculus@11729
   253
        SDL_free(data);
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@11730
   276
slouken@11730
   277
#endif
slouken@11730
   278
slouken@11730
   279
    data->mtldevice = layer.device;
icculus@11729
   280
    data->mtllayer = layer;
icculus@11729
   281
    data->mtlcmdqueue = [data->mtldevice newCommandQueue];
icculus@11729
   282
    data->mtlcmdqueue.label = @"SDL Metal Renderer";
icculus@11729
   283
icculus@11729
   284
    data->mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor];  // !!! FIXME: is this autoreleased?
icculus@11729
   285
icculus@11729
   286
    // we don't specify a depth or stencil buffer because the render API doesn't currently use them.
icculus@11729
   287
    MTLRenderPassColorAttachmentDescriptor *colorAttachment = data->mtlpassdesc.colorAttachments[0];
icculus@11729
   288
    data->mtlbackbuffer = [data->mtllayer nextDrawable];
icculus@11729
   289
    colorAttachment.texture = data->mtlbackbuffer.texture;
icculus@11729
   290
    colorAttachment.loadAction = MTLLoadActionClear;
icculus@11729
   291
    colorAttachment.clearColor = MTLClearColorMake(0.0f, 0.0f, 0.0f, 1.0f);
icculus@11729
   292
    data->mtlcmdbuffer = [data->mtlcmdqueue commandBuffer];
icculus@11729
   293
icculus@11729
   294
    // Just push a clear to the screen to start so we're in a good state.
icculus@11729
   295
    data->mtlcmdencoder = [data->mtlcmdbuffer renderCommandEncoderWithDescriptor:data->mtlpassdesc];
icculus@11729
   296
    data->mtlcmdencoder.label = @"Initial drawable clear";
icculus@11729
   297
icculus@11729
   298
    METAL_RenderPresent(renderer);
icculus@11729
   299
icculus@11729
   300
    renderer->WindowEvent = METAL_WindowEvent;
icculus@11729
   301
    renderer->GetOutputSize = METAL_GetOutputSize;
icculus@11729
   302
    renderer->CreateTexture = METAL_CreateTexture;
icculus@11729
   303
    renderer->UpdateTexture = METAL_UpdateTexture;
icculus@11729
   304
    renderer->UpdateTextureYUV = METAL_UpdateTextureYUV;
icculus@11729
   305
    renderer->LockTexture = METAL_LockTexture;
icculus@11729
   306
    renderer->UnlockTexture = METAL_UnlockTexture;
icculus@11729
   307
    renderer->SetRenderTarget = METAL_SetRenderTarget;
icculus@11729
   308
    renderer->UpdateViewport = METAL_UpdateViewport;
icculus@11729
   309
    renderer->UpdateClipRect = METAL_UpdateClipRect;
icculus@11729
   310
    renderer->RenderClear = METAL_RenderClear;
icculus@11729
   311
    renderer->RenderDrawPoints = METAL_RenderDrawPoints;
icculus@11729
   312
    renderer->RenderDrawLines = METAL_RenderDrawLines;
icculus@11729
   313
    renderer->RenderFillRects = METAL_RenderFillRects;
icculus@11729
   314
    renderer->RenderCopy = METAL_RenderCopy;
icculus@11729
   315
    renderer->RenderCopyEx = METAL_RenderCopyEx;
icculus@11729
   316
    renderer->RenderReadPixels = METAL_RenderReadPixels;
icculus@11729
   317
    renderer->RenderPresent = METAL_RenderPresent;
icculus@11729
   318
    renderer->DestroyTexture = METAL_DestroyTexture;
icculus@11729
   319
    renderer->DestroyRenderer = METAL_DestroyRenderer;
icculus@11729
   320
icculus@11729
   321
    renderer->info = METAL_RenderDriver.info;
icculus@11729
   322
    renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
icculus@11729
   323
icculus@11729
   324
    // !!! FIXME: how do you control this in Metal?
icculus@11729
   325
    renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
icculus@11729
   326
icculus@11729
   327
    NSError *err = nil;
icculus@11729
   328
slouken@11730
   329
    // The compiled .metallib is embedded in a static array in a header file
slouken@11730
   330
    // but the original shader source code is in SDL_shaders_metal.metal.
icculus@11729
   331
    dispatch_data_t mtllibdata = dispatch_data_create(sdl_metallib, sdl_metallib_len, dispatch_get_global_queue(0, 0), ^{});
icculus@11729
   332
    data->mtllibrary = [data->mtldevice newLibraryWithData:mtllibdata error:&err];
icculus@11729
   333
    SDL_assert(err == nil);
icculus@11729
   334
    dispatch_release(mtllibdata);
icculus@11729
   335
    data->mtllibrary.label = @"SDL Metal renderer shader library";
icculus@11729
   336
icculus@11729
   337
    MakePipelineStates(data, data->mtlpipelineprims, @"SDL primitives pipeline", @"SDL_Simple_vertex", @"SDL_Simple_fragment");
icculus@11729
   338
    MakePipelineStates(data, data->mtlpipelinecopy, @"SDL_RenderCopy pipeline", @"SDL_Copy_vertex", @"SDL_Copy_fragment");
icculus@11729
   339
icculus@11729
   340
    static const float clearverts[] = { -1, -1, -1, 1, 1, 1, 1, -1, -1, -1 };
icculus@11729
   341
    data->mtlbufclearverts = [data->mtldevice newBufferWithBytes:clearverts length:sizeof(clearverts) options:MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModePrivate];
icculus@11729
   342
    data->mtlbufclearverts.label = @"SDL_RenderClear vertices";
icculus@11729
   343
icculus@11729
   344
    // !!! FIXME: force more clears here so all the drawables are sane to start, and our static buffers are definitely flushed.
icculus@11729
   345
icculus@11729
   346
    return renderer;
icculus@11729
   347
}
icculus@11729
   348
icculus@11729
   349
static void
icculus@11729
   350
METAL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
icculus@11729
   351
{
icculus@11729
   352
    if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
icculus@11729
   353
        event->event == SDL_WINDOWEVENT_SHOWN ||
icculus@11729
   354
        event->event == SDL_WINDOWEVENT_HIDDEN) {
icculus@11729
   355
        // !!! FIXME: write me
icculus@11729
   356
    }
icculus@11729
   357
}
icculus@11729
   358
icculus@11729
   359
static int
icculus@11729
   360
METAL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
icculus@11729
   361
{
icculus@11729
   362
    METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
icculus@11729
   363
    *w = (int) data->mtlbackbuffer.texture.width;
icculus@11729
   364
    *h = (int) data->mtlbackbuffer.texture.height;
icculus@11729
   365
    return 0;
icculus@11729
   366
}
icculus@11729
   367
icculus@11729
   368
static int
icculus@11729
   369
METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
icculus@11729
   370
{
icculus@11729
   371
    METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
icculus@11729
   372
    MTLPixelFormat mtlpixfmt;
icculus@11729
   373
icculus@11729
   374
    switch (texture->format) {
icculus@11729
   375
        case SDL_PIXELFORMAT_ABGR8888: mtlpixfmt = MTLPixelFormatRGBA8Unorm; break;
icculus@11729
   376
        case SDL_PIXELFORMAT_ARGB8888: mtlpixfmt = MTLPixelFormatBGRA8Unorm; break;
icculus@11729
   377
        default: return SDL_SetError("Texture format %s not supported by Metal", SDL_GetPixelFormatName(texture->format));
icculus@11729
   378
    }
icculus@11729
   379
icculus@11729
   380
    // !!! FIXME: autorelease or nah?
icculus@11729
   381
    MTLTextureDescriptor *mtltexdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:mtlpixfmt
icculus@11729
   382
                                            width:(NSUInteger)texture->w height:(NSUInteger)texture->h mipmapped:NO];
icculus@11729
   383
icculus@11729
   384
    id<MTLTexture> mtltexture = [data->mtldevice newTextureWithDescriptor:mtltexdesc];
icculus@11729
   385
    [mtltexdesc release];
icculus@11729
   386
    if (mtltexture == nil) {
icculus@11729
   387
        return SDL_SetError("Texture allocation failed");
icculus@11729
   388
    }
icculus@11729
   389
icculus@11729
   390
    texture->driverdata = mtltexture;
icculus@11729
   391
icculus@11729
   392
    return 0;
icculus@11729
   393
}
icculus@11729
   394
icculus@11729
   395
static int
icculus@11729
   396
METAL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   397
                 const SDL_Rect * rect, const void *pixels, int pitch)
icculus@11729
   398
{
icculus@11729
   399
    // !!! FIXME: this is a synchronous call; it doesn't return until data is uploaded in some form.
icculus@11729
   400
    // !!! FIXME:  Maybe move this off to a thread that marks the texture as uploaded and only stall the main thread if we try to
icculus@11729
   401
    // !!! 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
   402
    // !!! FIXME:  stuff way ahead of time and/or using it immediately after upload?
icculus@11729
   403
    id<MTLTexture> mtltexture = (id<MTLTexture>) texture->driverdata;
icculus@11729
   404
    [mtltexture replaceRegion:MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h) mipmapLevel:0 withBytes:pixels bytesPerRow:pitch];
icculus@11729
   405
    return 0;
icculus@11729
   406
}
icculus@11729
   407
icculus@11729
   408
static int
icculus@11729
   409
METAL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   410
                    const SDL_Rect * rect,
icculus@11729
   411
                    const Uint8 *Yplane, int Ypitch,
icculus@11729
   412
                    const Uint8 *Uplane, int Upitch,
icculus@11729
   413
                    const Uint8 *Vplane, int Vpitch)
icculus@11729
   414
{
icculus@11729
   415
    return SDL_Unsupported();  // !!! FIXME
icculus@11729
   416
}
icculus@11729
   417
icculus@11729
   418
static int
icculus@11729
   419
METAL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   420
               const SDL_Rect * rect, void **pixels, int *pitch)
icculus@11729
   421
{
icculus@11729
   422
    return SDL_Unsupported();   // !!! FIXME: write me
icculus@11729
   423
}
icculus@11729
   424
icculus@11729
   425
static void
icculus@11729
   426
METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
icculus@11729
   427
{
icculus@11729
   428
    // !!! FIXME: write me
icculus@11729
   429
}
icculus@11729
   430
icculus@11729
   431
static int
icculus@11729
   432
METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
icculus@11729
   433
{
icculus@11729
   434
    METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
icculus@11729
   435
    id<MTLTexture> mtltexture = texture ? (id<MTLTexture>) texture->driverdata : nil;
icculus@11729
   436
    data->mtlpassdesc.colorAttachments[0].texture = mtltexture;
icculus@11729
   437
    return 0;
icculus@11729
   438
}
icculus@11729
   439
icculus@11729
   440
static int
icculus@11729
   441
METAL_UpdateViewport(SDL_Renderer * renderer)
icculus@11729
   442
{
icculus@11729
   443
    METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
icculus@11729
   444
    if (data->mtlcmdencoder != nil) {
icculus@11729
   445
        MTLViewport viewport;
icculus@11729
   446
        viewport.originX = renderer->viewport.x;
icculus@11729
   447
        viewport.originY = renderer->viewport.y;
icculus@11729
   448
        viewport.width = renderer->viewport.w;
icculus@11729
   449
        viewport.height = renderer->viewport.h;
icculus@11729
   450
        viewport.znear = 0.0;
icculus@11729
   451
        viewport.zfar = 1.0;
icculus@11729
   452
        [data->mtlcmdencoder setViewport:viewport];
icculus@11729
   453
    }
icculus@11729
   454
    return 0;
icculus@11729
   455
}
icculus@11729
   456
icculus@11729
   457
static int
icculus@11729
   458
METAL_UpdateClipRect(SDL_Renderer * renderer)
icculus@11729
   459
{
icculus@11729
   460
    // !!! FIXME: should this care about the viewport?
icculus@11729
   461
    METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
icculus@11729
   462
    if (data->mtlcmdencoder != nil) {
icculus@11729
   463
        MTLScissorRect mtlrect;
icculus@11729
   464
        if (renderer->clipping_enabled) {
icculus@11729
   465
            const SDL_Rect *rect = &renderer->clip_rect;
icculus@11729
   466
            mtlrect.x = renderer->viewport.x + rect->x;
icculus@11729
   467
            mtlrect.y = renderer->viewport.x + rect->y;
icculus@11729
   468
            mtlrect.width = rect->w;
icculus@11729
   469
            mtlrect.height = rect->h;
icculus@11729
   470
        } else {
icculus@11729
   471
            mtlrect.x = renderer->viewport.x;
icculus@11729
   472
            mtlrect.y = renderer->viewport.y;
icculus@11729
   473
            mtlrect.width = renderer->viewport.w;
icculus@11729
   474
            mtlrect.height = renderer->viewport.h;
icculus@11729
   475
        }
icculus@11729
   476
        [data->mtlcmdencoder setScissorRect:mtlrect];
icculus@11729
   477
    }
icculus@11729
   478
    return 0;
icculus@11729
   479
}
icculus@11729
   480
icculus@11729
   481
static int
icculus@11729
   482
METAL_RenderClear(SDL_Renderer * renderer)
icculus@11729
   483
{
icculus@11729
   484
    // We could dump the command buffer and force a clear on a new one, but this will respect the scissor state.
icculus@11729
   485
    METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
icculus@11729
   486
icculus@11729
   487
    // !!! FIXME: render color should live in a dedicated uniform buffer.
icculus@11729
   488
    const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
icculus@11729
   489
icculus@11729
   490
    MTLViewport viewport;  // RenderClear ignores the viewport state, though, so reset that.
icculus@11729
   491
    viewport.originX = viewport.originY = 0.0;
icculus@11729
   492
    viewport.width = data->mtlbackbuffer.texture.width;
icculus@11729
   493
    viewport.height = data->mtlbackbuffer.texture.height;
icculus@11729
   494
    viewport.znear = 0.0;
icculus@11729
   495
    viewport.zfar = 1.0;
icculus@11729
   496
icculus@11729
   497
    // Draw as if we're doing a simple filled rect to the screen now.
icculus@11729
   498
    [data->mtlcmdencoder setViewport:viewport];
icculus@11729
   499
    [data->mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data->mtlpipelineprims, renderer->blendMode)];
icculus@11729
   500
    [data->mtlcmdencoder setVertexBuffer:data->mtlbufclearverts offset:0 atIndex:0];
icculus@11729
   501
    [data->mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
icculus@11729
   502
    [data->mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:5];
icculus@11729
   503
icculus@11729
   504
    // reset the viewport for the rest of our usual drawing work...
icculus@11729
   505
    viewport.originX = renderer->viewport.x;
icculus@11729
   506
    viewport.originY = renderer->viewport.y;
icculus@11729
   507
    viewport.width = renderer->viewport.w;
icculus@11729
   508
    viewport.height = renderer->viewport.h;
icculus@11729
   509
    viewport.znear = 0.0;
icculus@11729
   510
    viewport.zfar = 1.0;
icculus@11729
   511
    [data->mtlcmdencoder setViewport:viewport];
icculus@11729
   512
icculus@11729
   513
    return 0;
icculus@11729
   514
}
icculus@11729
   515
icculus@11729
   516
// normalize a value from 0.0f to len into -1.0f to 1.0f.
icculus@11729
   517
static inline float
icculus@11729
   518
norm(const float _val, const float len)
icculus@11729
   519
{
icculus@11729
   520
    const float val = (_val < 0.0f) ? 0.0f : (_val > len) ? len : _val;
icculus@11729
   521
    return ((val / len) * 2.0f) - 1.0f;  // !!! FIXME: is this right?
icculus@11729
   522
}
icculus@11729
   523
icculus@11729
   524
// normalize a value from 0.0f to len into -1.0f to 1.0f.
icculus@11729
   525
static inline float
icculus@11729
   526
normy(const float _val, const float len)
icculus@11729
   527
{
icculus@11729
   528
    return norm(len - ((_val < 0.0f) ? 0.0f : (_val > len) ? len : _val), len);
icculus@11729
   529
}
icculus@11729
   530
icculus@11729
   531
// normalize a value from 0.0f to len into 0.0f to 1.0f.
icculus@11729
   532
static inline float
icculus@11729
   533
normtex(const float _val, const float len)
icculus@11729
   534
{
icculus@11729
   535
    const float val = (_val < 0.0f) ? 0.0f : (_val > len) ? len : _val;
icculus@11729
   536
    return (val / len);
icculus@11729
   537
}
icculus@11729
   538
icculus@11729
   539
static int
icculus@11729
   540
DrawVerts(SDL_Renderer * renderer, const SDL_FPoint * points, int count,
icculus@11729
   541
          const MTLPrimitiveType primtype)
icculus@11729
   542
{
icculus@11729
   543
    const size_t vertlen = (sizeof (float) * 2) * count;
icculus@11729
   544
    float *verts = SDL_malloc(vertlen);
icculus@11729
   545
    if (!verts) {
icculus@11729
   546
        return SDL_OutOfMemory();
icculus@11729
   547
    }
icculus@11729
   548
icculus@11729
   549
    METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
icculus@11729
   550
icculus@11729
   551
    // !!! FIXME: render color should live in a dedicated uniform buffer.
icculus@11729
   552
    const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
icculus@11729
   553
icculus@11729
   554
    [data->mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data->mtlpipelineprims, renderer->blendMode)];
icculus@11729
   555
    [data->mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
icculus@11729
   556
icculus@11729
   557
    const float w = (float) data->mtlpassdesc.colorAttachments[0].texture.width;
icculus@11729
   558
    const float h = (float) data->mtlpassdesc.colorAttachments[0].texture.height;
icculus@11729
   559
icculus@11729
   560
    // !!! FIXME: we can convert this in the shader. This will save the malloc and for-loop, but we still need to upload.
icculus@11729
   561
    float *ptr = verts;
icculus@11729
   562
    for (int i = 0; i < count; i++, points++) {
icculus@11729
   563
        *ptr = norm(points->x, w); ptr++;
icculus@11729
   564
        *ptr = normy(points->y, h); ptr++;
icculus@11729
   565
    }
icculus@11729
   566
icculus@11729
   567
    [data->mtlcmdencoder setVertexBytes:verts length:vertlen atIndex:0];
icculus@11729
   568
    [data->mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];
icculus@11729
   569
icculus@11729
   570
    SDL_free(verts);
icculus@11729
   571
    return 0;
icculus@11729
   572
}
icculus@11729
   573
icculus@11729
   574
static int
icculus@11729
   575
METAL_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points, int count)
icculus@11729
   576
{
icculus@11729
   577
    return DrawVerts(renderer, points, count, MTLPrimitiveTypePoint);
icculus@11729
   578
}
icculus@11729
   579
icculus@11729
   580
static int
icculus@11729
   581
METAL_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points, int count)
icculus@11729
   582
{
icculus@11729
   583
    return DrawVerts(renderer, points, count, MTLPrimitiveTypeLineStrip);
icculus@11729
   584
}
icculus@11729
   585
icculus@11729
   586
static int
icculus@11729
   587
METAL_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count)
icculus@11729
   588
{
icculus@11729
   589
    METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
icculus@11729
   590
icculus@11729
   591
    // !!! FIXME: render color should live in a dedicated uniform buffer.
icculus@11729
   592
    const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
icculus@11729
   593
icculus@11729
   594
    [data->mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data->mtlpipelineprims, renderer->blendMode)];
icculus@11729
   595
    [data->mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
icculus@11729
   596
icculus@11729
   597
    const float w = (float) data->mtlpassdesc.colorAttachments[0].texture.width;
icculus@11729
   598
    const float h = (float) data->mtlpassdesc.colorAttachments[0].texture.height;
icculus@11729
   599
icculus@11729
   600
    for (int i = 0; i < count; i++, rects++) {
icculus@11729
   601
        if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) continue;
icculus@11729
   602
icculus@11729
   603
        const float verts[] = {
icculus@11729
   604
            norm(rects->x, w), normy(rects->y + rects->h, h),
icculus@11729
   605
            norm(rects->x, w), normy(rects->y, h),
icculus@11729
   606
            norm(rects->x + rects->w, w), normy(rects->y, h),
icculus@11729
   607
            norm(rects->x, w), normy(rects->y + rects->h, h),
icculus@11729
   608
            norm(rects->x + rects->w, w), normy(rects->y + rects->h, h)
icculus@11729
   609
        };
icculus@11729
   610
icculus@11729
   611
        [data->mtlcmdencoder setVertexBytes:verts length:sizeof(verts) atIndex:0];
icculus@11729
   612
        [data->mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:5];
icculus@11729
   613
    }
icculus@11729
   614
icculus@11729
   615
    return 0;
icculus@11729
   616
}
icculus@11729
   617
icculus@11729
   618
static int
icculus@11729
   619
METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   620
              const SDL_Rect * srcrect, const SDL_FRect * dstrect)
icculus@11729
   621
{
icculus@11729
   622
    METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
icculus@11729
   623
    id<MTLTexture> mtltexture = (id<MTLTexture>) texture->driverdata;
icculus@11729
   624
    const float w = (float) data->mtlpassdesc.colorAttachments[0].texture.width;
icculus@11729
   625
    const float h = (float) data->mtlpassdesc.colorAttachments[0].texture.height;
icculus@11729
   626
    const float texw = (float) mtltexture.width;
icculus@11729
   627
    const float texh = (float) mtltexture.height;
icculus@11729
   628
icculus@11729
   629
    const float xy[] = {
icculus@11729
   630
        norm(dstrect->x, w), normy(dstrect->y + dstrect->h, h),
icculus@11729
   631
        norm(dstrect->x, w), normy(dstrect->y, h),
icculus@11729
   632
        norm(dstrect->x + dstrect->w, w), normy(dstrect->y, h),
icculus@11729
   633
        norm(dstrect->x, w), normy(dstrect->y + dstrect->h, h),
icculus@11729
   634
        norm(dstrect->x + dstrect->w, w), normy(dstrect->y + dstrect->h, h)
icculus@11729
   635
    };
icculus@11729
   636
icculus@11729
   637
    const float uv[] = {
icculus@11729
   638
        normtex(srcrect->x, texw), normtex(srcrect->y + srcrect->h, texh),
icculus@11729
   639
        normtex(srcrect->x, texw), normtex(srcrect->y, texh),
icculus@11729
   640
        normtex(srcrect->x + srcrect->w, texw), normtex(srcrect->y, texh),
icculus@11729
   641
        normtex(srcrect->x, texw), normtex(srcrect->y + srcrect->h, texh),
icculus@11729
   642
        normtex(srcrect->x + srcrect->w, texw), normtex(srcrect->y + srcrect->h, texh)
icculus@11729
   643
    };
icculus@11729
   644
icculus@11729
   645
    float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
icculus@11729
   646
    if (texture->modMode) {
icculus@11729
   647
        color[0] = ((float)texture->r) / 255.0f;
icculus@11729
   648
        color[1] = ((float)texture->g) / 255.0f;
icculus@11729
   649
        color[2] = ((float)texture->b) / 255.0f;
icculus@11729
   650
        color[3] = ((float)texture->a) / 255.0f;
icculus@11729
   651
    }
icculus@11729
   652
icculus@11729
   653
    [data->mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data->mtlpipelinecopy, texture->blendMode)];
icculus@11729
   654
    [data->mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
icculus@11729
   655
    [data->mtlcmdencoder setFragmentTexture:mtltexture atIndex:0];
icculus@11729
   656
    [data->mtlcmdencoder setVertexBytes:xy length:sizeof(xy) atIndex:0];
icculus@11729
   657
    [data->mtlcmdencoder setVertexBytes:uv length:sizeof(uv) atIndex:1];
icculus@11729
   658
    [data->mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:5];
icculus@11729
   659
icculus@11729
   660
    return 0;
icculus@11729
   661
}
icculus@11729
   662
icculus@11729
   663
static int
icculus@11729
   664
METAL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   665
              const SDL_Rect * srcrect, const SDL_FRect * dstrect,
icculus@11729
   666
              const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
icculus@11729
   667
{
icculus@11729
   668
    return SDL_Unsupported();  // !!! FIXME
icculus@11729
   669
}
icculus@11729
   670
icculus@11729
   671
static int
icculus@11729
   672
METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
icculus@11729
   673
                    Uint32 pixel_format, void * pixels, int pitch)
icculus@11729
   674
{
icculus@11729
   675
    METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
icculus@11729
   676
    MTLRenderPassColorAttachmentDescriptor *colorAttachment = data->mtlpassdesc.colorAttachments[0];
icculus@11729
   677
    id<MTLTexture> mtltexture = colorAttachment.texture;
icculus@11729
   678
    MTLRegion mtlregion;
icculus@11729
   679
icculus@11729
   680
    mtlregion.origin.x = rect->x;
icculus@11729
   681
    mtlregion.origin.y = rect->y;
icculus@11729
   682
    mtlregion.origin.z = 0;
icculus@11729
   683
    mtlregion.size.width = rect->w;
icculus@11729
   684
    mtlregion.size.height = rect->w;
icculus@11729
   685
    mtlregion.size.depth = 1;
icculus@11729
   686
icculus@11729
   687
    // we only do BGRA8 or RGBA8 at the moment, so 4 will do.
icculus@11729
   688
    const int temp_pitch = rect->w * 4;
icculus@11729
   689
    void *temp_pixels = SDL_malloc(temp_pitch * rect->h);
icculus@11729
   690
    if (!temp_pixels) {
icculus@11729
   691
        return SDL_OutOfMemory();
icculus@11729
   692
    }
icculus@11729
   693
icculus@11729
   694
    [mtltexture getBytes:temp_pixels bytesPerRow:temp_pitch fromRegion:mtlregion mipmapLevel:0];
icculus@11729
   695
icculus@11729
   696
    const Uint32 temp_format = (mtltexture.pixelFormat == MTLPixelFormatBGRA8Unorm) ? SDL_PIXELFORMAT_ARGB8888 : SDL_PIXELFORMAT_ABGR8888;
icculus@11729
   697
    const int status = SDL_ConvertPixels(rect->w, rect->h, temp_format, temp_pixels, temp_pitch, pixel_format, pixels, pitch);
icculus@11729
   698
    SDL_free(temp_pixels);
icculus@11729
   699
    return status;
icculus@11729
   700
}
icculus@11729
   701
icculus@11729
   702
static void
icculus@11729
   703
METAL_RenderPresent(SDL_Renderer * renderer)
icculus@11729
   704
{
icculus@11729
   705
    METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
icculus@11729
   706
    MTLRenderPassColorAttachmentDescriptor *colorAttachment = data->mtlpassdesc.colorAttachments[0];
icculus@11729
   707
    id<CAMetalDrawable> mtlbackbuffer = data->mtlbackbuffer;
icculus@11729
   708
icculus@11729
   709
    [data->mtlcmdencoder endEncoding];
icculus@11729
   710
    [data->mtlcmdbuffer presentDrawable:mtlbackbuffer];
icculus@11729
   711
icculus@11729
   712
    [data->mtlcmdbuffer addCompletedHandler:^(id <MTLCommandBuffer> mtlcmdbuffer){
icculus@11729
   713
        [mtlbackbuffer release];
icculus@11729
   714
    }];
icculus@11729
   715
icculus@11729
   716
    [data->mtlcmdbuffer commit];
icculus@11729
   717
icculus@11729
   718
    // Start next frame, once we can.
icculus@11729
   719
    // we don't specify a depth or stencil buffer because the render API doesn't currently use them.
icculus@11729
   720
    data->mtlbackbuffer = [data->mtllayer nextDrawable];
icculus@11729
   721
    SDL_assert(data->mtlbackbuffer);
icculus@11729
   722
    colorAttachment.texture = data->mtlbackbuffer.texture;
icculus@11729
   723
    colorAttachment.loadAction = MTLLoadActionDontCare;
icculus@11729
   724
    data->mtlcmdbuffer = [data->mtlcmdqueue commandBuffer];
icculus@11729
   725
    data->mtlcmdencoder = [data->mtlcmdbuffer renderCommandEncoderWithDescriptor:data->mtlpassdesc];
icculus@11729
   726
    data->mtlcmdencoder.label = @"SDL metal renderer frame";
icculus@11729
   727
icculus@11729
   728
    // Set up our current renderer state for the next frame...
icculus@11729
   729
    METAL_UpdateViewport(renderer);
icculus@11729
   730
    METAL_UpdateClipRect(renderer);
icculus@11729
   731
}
icculus@11729
   732
icculus@11729
   733
static void
icculus@11729
   734
METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
icculus@11729
   735
{
icculus@11729
   736
    id<MTLTexture> mtltexture = (id<MTLTexture>) texture->driverdata;
icculus@11729
   737
    [mtltexture release];
icculus@11729
   738
    texture->driverdata = NULL;
icculus@11729
   739
}
icculus@11729
   740
icculus@11729
   741
static void
icculus@11729
   742
METAL_DestroyRenderer(SDL_Renderer * renderer)
icculus@11729
   743
{
icculus@11729
   744
    METAL_RenderData *data = (METAL_RenderData *) renderer->driverdata;
icculus@11729
   745
icculus@11729
   746
    if (data) {
icculus@11729
   747
        int i;
icculus@11729
   748
        [data->mtlcmdencoder endEncoding];
icculus@11729
   749
        [data->mtlcmdencoder release];
icculus@11729
   750
        [data->mtlcmdbuffer release];
icculus@11729
   751
        [data->mtlcmdqueue release];
icculus@11729
   752
        for (i = 0; i < 4; i++) {
icculus@11729
   753
            [data->mtlpipelineprims[i] release];
icculus@11729
   754
            [data->mtlpipelinecopy[i] release];
icculus@11729
   755
        }
icculus@11729
   756
        [data->mtlbufclearverts release];
icculus@11729
   757
        [data->mtllibrary release];
icculus@11729
   758
        [data->mtldevice release];
icculus@11729
   759
        [data->mtlpassdesc release];
icculus@11729
   760
        [data->mtllayer release];
icculus@11729
   761
        SDL_free(data);
icculus@11729
   762
    }
icculus@11729
   763
    SDL_free(renderer);
icculus@11729
   764
}
icculus@11729
   765
icculus@11729
   766
#endif /* SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED */
icculus@11729
   767
icculus@11729
   768
/* vi: set ts=4 sw=4 expandtab: */