src/render/metal/SDL_render_metal.m
author Sam Lantinga
Tue, 02 Jan 2018 14:32:15 -0800
changeset 11807 6b3d9e08c586
parent 11806 aa740c67a96a
child 11809 4b858abfb24d
permissions -rw-r--r--
Fixed direction of y adjustment for new orthographic projection in the metal renderer
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__
slime73@11798
    32
#include "../../video/cocoa/SDL_cocoametalview.h"
slouken@11732
    33
#else
slouken@11732
    34
#include "../../video/uikit/SDL_uikitmetalview.h"
slouken@11732
    35
#endif
slime73@11798
    36
#import <Metal/Metal.h>
slime73@11798
    37
#import <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);
slime73@11800
    52
static SDL_bool METAL_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode);
icculus@11729
    53
static int METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
icculus@11729
    54
static int METAL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    55
                            const SDL_Rect * rect, const void *pixels,
icculus@11729
    56
                            int pitch);
icculus@11729
    57
static int METAL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    58
                               const SDL_Rect * rect,
icculus@11729
    59
                               const Uint8 *Yplane, int Ypitch,
icculus@11729
    60
                               const Uint8 *Uplane, int Upitch,
icculus@11729
    61
                               const Uint8 *Vplane, int Vpitch);
icculus@11729
    62
static int METAL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    63
                          const SDL_Rect * rect, void **pixels, int *pitch);
icculus@11729
    64
static void METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
icculus@11729
    65
static int METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
icculus@11729
    66
static int METAL_UpdateViewport(SDL_Renderer * renderer);
icculus@11729
    67
static int METAL_UpdateClipRect(SDL_Renderer * renderer);
icculus@11729
    68
static int METAL_RenderClear(SDL_Renderer * renderer);
icculus@11729
    69
static int METAL_RenderDrawPoints(SDL_Renderer * renderer,
icculus@11729
    70
                               const SDL_FPoint * points, int count);
icculus@11729
    71
static int METAL_RenderDrawLines(SDL_Renderer * renderer,
icculus@11729
    72
                              const SDL_FPoint * points, int count);
icculus@11729
    73
static int METAL_RenderFillRects(SDL_Renderer * renderer,
icculus@11729
    74
                              const SDL_FRect * rects, int count);
icculus@11729
    75
static int METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    76
                         const SDL_Rect * srcrect, const SDL_FRect * dstrect);
icculus@11729
    77
static int METAL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    78
                         const SDL_Rect * srcrect, const SDL_FRect * dstrect,
icculus@11729
    79
                         const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
icculus@11729
    80
static int METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
icculus@11729
    81
                               Uint32 pixel_format, void * pixels, int pitch);
icculus@11729
    82
static void METAL_RenderPresent(SDL_Renderer * renderer);
icculus@11729
    83
static void METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture);
icculus@11729
    84
static void METAL_DestroyRenderer(SDL_Renderer * renderer);
slouken@11744
    85
static void *METAL_GetMetalLayer(SDL_Renderer * renderer);
slouken@11744
    86
static void *METAL_GetMetalCommandEncoder(SDL_Renderer * renderer);
icculus@11729
    87
icculus@11729
    88
SDL_RenderDriver METAL_RenderDriver = {
icculus@11729
    89
    METAL_CreateRenderer,
icculus@11729
    90
    {
icculus@11729
    91
     "metal",
icculus@11729
    92
     (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE),
icculus@11729
    93
     2,
icculus@11729
    94
     {SDL_PIXELFORMAT_ARGB8888, SDL_PIXELFORMAT_ABGR8888},
icculus@11749
    95
icculus@11749
    96
     // !!! FIXME: how do you query Metal for this?
icculus@11749
    97
     // (the weakest GPU supported by Metal on iOS has 4k texture max, and
icculus@11749
    98
     //  other models might be 2x or 4x more. On macOS, it's 16k across the
icculus@11749
    99
     //  board right now.)
slouken@11751
   100
#ifdef __MACOSX__
slouken@11751
   101
     16384, 16384
slouken@11751
   102
#else
slouken@11751
   103
     4096, 4096
slouken@11751
   104
#endif
slouken@11751
   105
    }
icculus@11729
   106
};
icculus@11729
   107
slime73@11800
   108
typedef enum SDL_MetalVertexFunction
slime73@11800
   109
{
slime73@11800
   110
    SDL_METAL_VERTEX_SOLID,
slime73@11800
   111
    SDL_METAL_VERTEX_COPY,
slime73@11800
   112
} SDL_MetalVertexFunction;
slime73@11800
   113
slime73@11800
   114
typedef enum SDL_MetalFragmentFunction
slime73@11800
   115
{
slime73@11800
   116
    SDL_METAL_FRAGMENT_SOLID,
slime73@11801
   117
    SDL_METAL_FRAGMENT_COPY,
slime73@11800
   118
} SDL_MetalFragmentFunction;
slime73@11800
   119
slime73@11800
   120
typedef struct METAL_PipelineState
slime73@11800
   121
{
slime73@11800
   122
    SDL_BlendMode blendMode;
slime73@11800
   123
    void *pipe;
slime73@11800
   124
} METAL_PipelineState;
slime73@11800
   125
slime73@11800
   126
typedef struct METAL_PipelineCache
slime73@11800
   127
{
slime73@11800
   128
    METAL_PipelineState *states;
slime73@11800
   129
    int count;
slime73@11800
   130
    SDL_MetalVertexFunction vertexFunction;
slime73@11800
   131
    SDL_MetalFragmentFunction fragmentFunction;
slime73@11800
   132
    const char *label;
slime73@11800
   133
} METAL_PipelineCache;
slime73@11800
   134
slouken@11732
   135
@interface METAL_RenderData : NSObject
slouken@11735
   136
    @property (nonatomic, assign) BOOL beginScene;
slouken@11735
   137
    @property (nonatomic, retain) id<MTLDevice> mtldevice;
slouken@11735
   138
    @property (nonatomic, retain) id<MTLCommandQueue> mtlcmdqueue;
slouken@11735
   139
    @property (nonatomic, retain) id<MTLCommandBuffer> mtlcmdbuffer;
slouken@11735
   140
    @property (nonatomic, retain) id<MTLRenderCommandEncoder> mtlcmdencoder;
slouken@11735
   141
    @property (nonatomic, retain) id<MTLLibrary> mtllibrary;
slouken@11735
   142
    @property (nonatomic, retain) id<CAMetalDrawable> mtlbackbuffer;
slime73@11804
   143
    @property (nonatomic, assign) METAL_PipelineCache *mtlpipelineprims;
slime73@11804
   144
    @property (nonatomic, assign) METAL_PipelineCache *mtlpipelinecopy;
slime73@11801
   145
    @property (nonatomic, retain) id<MTLSamplerState> mtlsamplernearest;
slime73@11801
   146
    @property (nonatomic, retain) id<MTLSamplerState> mtlsamplerlinear;
slouken@11735
   147
    @property (nonatomic, retain) id<MTLBuffer> mtlbufclearverts;
slime73@11799
   148
    @property (nonatomic, retain) id<MTLBuffer> mtlbufidentitytransform;
slouken@11735
   149
    @property (nonatomic, retain) CAMetalLayer *mtllayer;
slouken@11735
   150
    @property (nonatomic, retain) MTLRenderPassDescriptor *mtlpassdesc;
slouken@11732
   151
@end
icculus@11729
   152
slouken@11732
   153
@implementation METAL_RenderData
slime73@11805
   154
#if !__has_feature(objc_arc)
slime73@11804
   155
- (void)dealloc
slime73@11804
   156
{
slime73@11804
   157
    [_mtldevice release];
slime73@11804
   158
    [_mtlcmdqueue release];
slime73@11804
   159
    [_mtlcmdbuffer release];
slime73@11804
   160
    [_mtlcmdencoder release];
slime73@11804
   161
    [_mtllibrary release];
slime73@11804
   162
    [_mtlbackbuffer release];
slime73@11804
   163
    [_mtlsamplernearest release];
slime73@11804
   164
    [_mtlsamplerlinear release];
slime73@11804
   165
    [_mtlbufclearverts release];
slime73@11804
   166
    [_mtlbufidentitytransform release];
slime73@11804
   167
    [_mtllayer release];
slime73@11804
   168
    [_mtlpassdesc release];
slime73@11804
   169
    [super dealloc];
slime73@11804
   170
}
slime73@11804
   171
#endif
slouken@11732
   172
@end
icculus@11729
   173
slouken@11753
   174
@interface METAL_TextureData : NSObject
slouken@11753
   175
    @property (nonatomic, retain) id<MTLTexture> mtltexture;
slime73@11801
   176
    @property (nonatomic, retain) id<MTLSamplerState> mtlsampler;
slouken@11753
   177
@end
slouken@11753
   178
slouken@11753
   179
@implementation METAL_TextureData
slime73@11805
   180
#if !__has_feature(objc_arc)
slime73@11804
   181
- (void)dealloc
slime73@11804
   182
{
slime73@11804
   183
    [_mtltexture release];
slime73@11804
   184
    [_mtlsampler release];
slime73@11804
   185
    [super dealloc];
slime73@11804
   186
}
slime73@11804
   187
#endif
slouken@11753
   188
@end
slouken@11753
   189
icculus@11729
   190
static int
icculus@11729
   191
IsMetalAvailable(const SDL_SysWMinfo *syswm)
icculus@11729
   192
{
slouken@11733
   193
    if (syswm->subsystem != SDL_SYSWM_COCOA && syswm->subsystem != SDL_SYSWM_UIKIT) {
slouken@11733
   194
        return SDL_SetError("Metal render target only supports Cocoa and UIKit video targets at the moment.");
icculus@11729
   195
    }
icculus@11729
   196
icculus@11729
   197
    // this checks a weak symbol.
icculus@11742
   198
#if (defined(__MACOSX__) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101100))
icculus@11729
   199
    if (MTLCreateSystemDefaultDevice == NULL) {  // probably on 10.10 or lower.
icculus@11729
   200
        return SDL_SetError("Metal framework not available on this system");
icculus@11729
   201
    }
icculus@11729
   202
#endif
icculus@11729
   203
icculus@11729
   204
    return 0;
icculus@11729
   205
}
icculus@11729
   206
slime73@11800
   207
static const MTLBlendOperation invalidBlendOperation = (MTLBlendOperation)0xFFFFFFFF;
slime73@11800
   208
static const MTLBlendFactor invalidBlendFactor = (MTLBlendFactor)0xFFFFFFFF;
slime73@11800
   209
slime73@11800
   210
static MTLBlendOperation
slime73@11800
   211
GetBlendOperation(SDL_BlendOperation operation)
slime73@11800
   212
{
slime73@11800
   213
    switch (operation) {
slime73@11800
   214
        case SDL_BLENDOPERATION_ADD: return MTLBlendOperationAdd;
slime73@11800
   215
        case SDL_BLENDOPERATION_SUBTRACT: return MTLBlendOperationSubtract;
slime73@11800
   216
        case SDL_BLENDOPERATION_REV_SUBTRACT: return MTLBlendOperationReverseSubtract;
slime73@11800
   217
        case SDL_BLENDOPERATION_MINIMUM: return MTLBlendOperationMin;
slime73@11800
   218
        case SDL_BLENDOPERATION_MAXIMUM: return MTLBlendOperationMax;
slime73@11800
   219
        default: return invalidBlendOperation;
slime73@11800
   220
    }
slime73@11800
   221
}
slime73@11800
   222
slime73@11800
   223
static MTLBlendFactor
slime73@11800
   224
GetBlendFactor(SDL_BlendFactor factor)
slime73@11800
   225
{
slime73@11800
   226
    switch (factor) {
slime73@11800
   227
        case SDL_BLENDFACTOR_ZERO: return MTLBlendFactorZero;
slime73@11800
   228
        case SDL_BLENDFACTOR_ONE: return MTLBlendFactorOne;
slime73@11800
   229
        case SDL_BLENDFACTOR_SRC_COLOR: return MTLBlendFactorSourceColor;
slime73@11800
   230
        case SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return MTLBlendFactorOneMinusSourceColor;
slime73@11800
   231
        case SDL_BLENDFACTOR_SRC_ALPHA: return MTLBlendFactorSourceAlpha;
slime73@11800
   232
        case SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return MTLBlendFactorOneMinusSourceAlpha;
slime73@11800
   233
        case SDL_BLENDFACTOR_DST_COLOR: return MTLBlendFactorDestinationColor;
slime73@11800
   234
        case SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR: return MTLBlendFactorOneMinusDestinationColor;
slime73@11800
   235
        case SDL_BLENDFACTOR_DST_ALPHA: return MTLBlendFactorDestinationAlpha;
slime73@11800
   236
        case SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return MTLBlendFactorOneMinusDestinationAlpha;
slime73@11800
   237
        default: return invalidBlendFactor;
slime73@11800
   238
    }
slime73@11800
   239
}
slime73@11800
   240
slime73@11800
   241
static NSString *
slime73@11800
   242
GetVertexFunctionName(SDL_MetalVertexFunction function)
slime73@11800
   243
{
slime73@11800
   244
    switch (function) {
slime73@11800
   245
        case SDL_METAL_VERTEX_SOLID: return @"SDL_Solid_vertex";
slime73@11800
   246
        case SDL_METAL_VERTEX_COPY: return @"SDL_Copy_vertex";
slime73@11800
   247
        default: return nil;
slime73@11800
   248
    }
slime73@11800
   249
}
slime73@11800
   250
slime73@11800
   251
static NSString *
slime73@11800
   252
GetFragmentFunctionName(SDL_MetalFragmentFunction function)
slime73@11800
   253
{
slime73@11800
   254
    switch (function) {
slime73@11800
   255
        case SDL_METAL_FRAGMENT_SOLID: return @"SDL_Solid_fragment";
slime73@11801
   256
        case SDL_METAL_FRAGMENT_COPY: return @"SDL_Copy_fragment";
slime73@11800
   257
        default: return nil;
slime73@11800
   258
    }
slime73@11800
   259
}
slime73@11800
   260
icculus@11729
   261
static id<MTLRenderPipelineState>
slime73@11800
   262
MakePipelineState(METAL_RenderData *data, METAL_PipelineCache *cache,
slime73@11800
   263
                  NSString *blendlabel, SDL_BlendMode blendmode)
icculus@11729
   264
{
slime73@11800
   265
    id<MTLFunction> mtlvertfn = [data.mtllibrary newFunctionWithName:GetVertexFunctionName(cache->vertexFunction)];
slime73@11800
   266
    id<MTLFunction> mtlfragfn = [data.mtllibrary newFunctionWithName:GetFragmentFunctionName(cache->fragmentFunction)];
icculus@11729
   267
    SDL_assert(mtlvertfn != nil);
icculus@11729
   268
    SDL_assert(mtlfragfn != nil);
icculus@11729
   269
icculus@11729
   270
    MTLRenderPipelineDescriptor *mtlpipedesc = [[MTLRenderPipelineDescriptor alloc] init];
icculus@11729
   271
    mtlpipedesc.vertexFunction = mtlvertfn;
icculus@11729
   272
    mtlpipedesc.fragmentFunction = mtlfragfn;
slime73@11800
   273
slime73@11800
   274
    MTLRenderPipelineColorAttachmentDescriptor *rtdesc = mtlpipedesc.colorAttachments[0];
icculus@11729
   275
slime73@11800
   276
    // !!! FIXME: This should be part of the pipeline state cache.
slime73@11800
   277
    rtdesc.pixelFormat = data.mtllayer.pixelFormat;
icculus@11729
   278
slime73@11800
   279
    if (blendmode != SDL_BLENDMODE_NONE) {
slime73@11800
   280
        rtdesc.blendingEnabled = YES;
slime73@11800
   281
        rtdesc.sourceRGBBlendFactor = GetBlendFactor(SDL_GetBlendModeSrcColorFactor(blendmode));
slime73@11800
   282
        rtdesc.destinationRGBBlendFactor = GetBlendFactor(SDL_GetBlendModeDstColorFactor(blendmode));
slime73@11800
   283
        rtdesc.rgbBlendOperation = GetBlendOperation(SDL_GetBlendModeColorOperation(blendmode));
slime73@11800
   284
        rtdesc.sourceAlphaBlendFactor = GetBlendFactor(SDL_GetBlendModeSrcAlphaFactor(blendmode));
slime73@11800
   285
        rtdesc.destinationAlphaBlendFactor = GetBlendFactor(SDL_GetBlendModeDstAlphaFactor(blendmode));
slime73@11800
   286
        rtdesc.alphaBlendOperation = GetBlendOperation(SDL_GetBlendModeAlphaOperation(blendmode));
slime73@11800
   287
    } else {
slime73@11800
   288
        rtdesc.blendingEnabled = NO;
icculus@11729
   289
    }
icculus@11729
   290
slime73@11800
   291
    mtlpipedesc.label = [@(cache->label) stringByAppendingString:blendlabel];
icculus@11729
   292
icculus@11729
   293
    NSError *err = nil;
slime73@11800
   294
    id<MTLRenderPipelineState> state = [data.mtldevice newRenderPipelineStateWithDescriptor:mtlpipedesc error:&err];
icculus@11729
   295
    SDL_assert(err == nil);
slime73@11800
   296
slime73@11800
   297
    METAL_PipelineState pipeline;
slime73@11800
   298
    pipeline.blendMode = blendmode;
slime73@11800
   299
    pipeline.pipe = (void *)CFBridgingRetain(state);
slime73@11800
   300
slime73@11800
   301
    METAL_PipelineState *states = SDL_realloc(cache->states, (cache->count + 1) * sizeof(pipeline));
slime73@11800
   302
slouken@11732
   303
#if !__has_feature(objc_arc)
icculus@11729
   304
    [mtlpipedesc release];  // !!! FIXME: can these be reused for each creation, or does the pipeline obtain it?
icculus@11729
   305
    [mtlvertfn release];
icculus@11729
   306
    [mtlfragfn release];
slime73@11800
   307
    [state release];
slouken@11732
   308
#endif
slime73@11800
   309
slime73@11800
   310
    if (states) {
slime73@11800
   311
        states[cache->count++] = pipeline;
slime73@11800
   312
        cache->states = states;
slime73@11800
   313
        return (__bridge id<MTLRenderPipelineState>)pipeline.pipe;
slime73@11800
   314
    } else {
slime73@11800
   315
        CFBridgingRelease(pipeline.pipe);
slime73@11800
   316
        SDL_OutOfMemory();
slime73@11800
   317
        return NULL;
slime73@11800
   318
    }
slime73@11800
   319
}
slime73@11800
   320
slime73@11800
   321
static METAL_PipelineCache *
slime73@11800
   322
MakePipelineCache(METAL_RenderData *data, const char *label, SDL_MetalVertexFunction vertfn, SDL_MetalFragmentFunction fragfn)
slime73@11800
   323
{
slime73@11800
   324
    METAL_PipelineCache *cache = SDL_malloc(sizeof(METAL_PipelineCache));
slime73@11800
   325
slime73@11800
   326
    if (!cache) {
slime73@11800
   327
        SDL_OutOfMemory();
slime73@11800
   328
        return NULL;
slime73@11800
   329
    }
slime73@11800
   330
slime73@11800
   331
    SDL_zerop(cache);
slime73@11800
   332
slime73@11800
   333
    cache->vertexFunction = vertfn;
slime73@11800
   334
    cache->fragmentFunction = fragfn;
slime73@11800
   335
    cache->label = label;
slime73@11800
   336
slime73@11800
   337
    /* Create pipeline states for the default blend modes. Custom blend modes
slime73@11800
   338
     * will be added to the cache on-demand. */
slime73@11800
   339
    MakePipelineState(data, cache, @"(blend=none)", SDL_BLENDMODE_NONE);
slime73@11800
   340
    MakePipelineState(data, cache, @"(blend=blend)", SDL_BLENDMODE_BLEND);
slime73@11800
   341
    MakePipelineState(data, cache, @"(blend=add)", SDL_BLENDMODE_ADD);
slime73@11800
   342
    MakePipelineState(data, cache, @"(blend=mod)", SDL_BLENDMODE_MOD);
slime73@11800
   343
slime73@11800
   344
    return cache;
icculus@11729
   345
}
icculus@11729
   346
icculus@11729
   347
static void
slime73@11800
   348
DestroyPipelineCache(METAL_PipelineCache *cache)
icculus@11729
   349
{
slime73@11800
   350
    if (cache != NULL) {
slime73@11800
   351
        for (int i = 0; i < cache->count; i++) {
slime73@11800
   352
            CFBridgingRelease(cache->states[i].pipe);
slime73@11800
   353
        }
slime73@11800
   354
slime73@11800
   355
        SDL_free(cache->states);
slime73@11800
   356
        SDL_free(cache);
slime73@11800
   357
    }
icculus@11729
   358
}
icculus@11729
   359
icculus@11729
   360
static inline id<MTLRenderPipelineState>
slime73@11800
   361
ChoosePipelineState(METAL_RenderData *data, METAL_PipelineCache *cache, const SDL_BlendMode blendmode)
icculus@11729
   362
{
slime73@11800
   363
    for (int i = 0; i < cache->count; i++) {
slime73@11800
   364
        if (cache->states[i].blendMode == blendmode) {
slime73@11800
   365
            return (__bridge id<MTLRenderPipelineState>)cache->states[i].pipe;
slime73@11800
   366
        }
icculus@11729
   367
    }
slime73@11800
   368
slime73@11800
   369
    return MakePipelineState(data, cache, [NSString stringWithFormat:@"(blend=custom 0x%x)", blendmode], blendmode);
icculus@11729
   370
}
icculus@11729
   371
icculus@11729
   372
static SDL_Renderer *
icculus@11729
   373
METAL_CreateRenderer(SDL_Window * window, Uint32 flags)
icculus@11729
   374
{
icculus@11729
   375
    SDL_Renderer *renderer = NULL;
icculus@11729
   376
    METAL_RenderData *data = NULL;
icculus@11729
   377
    SDL_SysWMinfo syswm;
icculus@11729
   378
icculus@11729
   379
    SDL_VERSION(&syswm.version);
icculus@11729
   380
    if (!SDL_GetWindowWMInfo(window, &syswm)) {
icculus@11729
   381
        return NULL;
icculus@11729
   382
    }
icculus@11729
   383
icculus@11729
   384
    if (IsMetalAvailable(&syswm) == -1) {
icculus@11729
   385
        return NULL;
icculus@11729
   386
    }
icculus@11729
   387
slouken@11732
   388
    renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
slouken@11732
   389
    if (!renderer) {
icculus@11729
   390
        SDL_OutOfMemory();
icculus@11729
   391
        return NULL;
icculus@11729
   392
    }
icculus@11729
   393
slouken@11732
   394
    data = [[METAL_RenderData alloc] init];
slouken@11735
   395
    data.beginScene = YES;
icculus@11729
   396
slouken@11732
   397
    renderer->driverdata = (void*)CFBridgingRetain(data);
icculus@11729
   398
    renderer->window = window;
icculus@11729
   399
slouken@11730
   400
#ifdef __MACOSX__
slouken@11730
   401
    id<MTLDevice> mtldevice = MTLCreateSystemDefaultDevice();  // !!! FIXME: MTLCopyAllDevices() can find other GPUs...
slouken@11730
   402
    if (mtldevice == nil) {
icculus@11729
   403
        SDL_free(renderer);
slouken@11732
   404
#if !__has_feature(objc_arc)
slouken@11732
   405
        [data release];
slouken@11732
   406
#endif
icculus@11729
   407
        SDL_SetError("Failed to obtain Metal device");
icculus@11729
   408
        return NULL;
icculus@11729
   409
    }
icculus@11729
   410
icculus@11729
   411
    // !!! FIXME: error checking on all of this.
icculus@11729
   412
slime73@11798
   413
    NSView *view = Cocoa_Mtl_AddMetalView(window);
slime73@11798
   414
    CAMetalLayer *layer = (CAMetalLayer *)[view layer];
icculus@11729
   415
slouken@11730
   416
    layer.device = mtldevice;
slime73@11798
   417
icculus@11729
   418
    //layer.colorspace = nil;
icculus@11729
   419
slouken@11730
   420
#else
slouken@11732
   421
    UIView *view = UIKit_Mtl_AddMetalView(window);
slouken@11732
   422
    CAMetalLayer *layer = (CAMetalLayer *)[view layer];
slouken@11730
   423
#endif
slouken@11730
   424
slime73@11800
   425
    // Necessary for RenderReadPixels.
slime73@11800
   426
    layer.framebufferOnly = NO;
slime73@11800
   427
slouken@11732
   428
    data.mtldevice = layer.device;
slouken@11732
   429
    data.mtllayer = layer;
slime73@11804
   430
    id<MTLCommandQueue> mtlcmdqueue = [data.mtldevice newCommandQueue];
slime73@11804
   431
    data.mtlcmdqueue = mtlcmdqueue;
slouken@11732
   432
    data.mtlcmdqueue.label = @"SDL Metal Renderer";
icculus@11749
   433
    data.mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor];
icculus@11729
   434
slouken@11735
   435
    NSError *err = nil;
icculus@11729
   436
slouken@11735
   437
    // The compiled .metallib is embedded in a static array in a header file
slouken@11735
   438
    // but the original shader source code is in SDL_shaders_metal.metal.
slouken@11735
   439
    dispatch_data_t mtllibdata = dispatch_data_create(sdl_metallib, sdl_metallib_len, dispatch_get_global_queue(0, 0), ^{});
slime73@11804
   440
    id<MTLLibrary> mtllibrary = [data.mtldevice newLibraryWithData:mtllibdata error:&err];
slime73@11804
   441
    data.mtllibrary = mtllibrary;
slouken@11735
   442
    SDL_assert(err == nil);
slouken@11735
   443
#if !__has_feature(objc_arc)
slouken@11735
   444
    dispatch_release(mtllibdata);
slouken@11735
   445
#endif
slouken@11735
   446
    data.mtllibrary.label = @"SDL Metal renderer shader library";
icculus@11729
   447
slime73@11800
   448
    data.mtlpipelineprims = MakePipelineCache(data, "SDL primitives pipeline ", SDL_METAL_VERTEX_SOLID, SDL_METAL_FRAGMENT_SOLID);
slime73@11801
   449
    data.mtlpipelinecopy = MakePipelineCache(data, "SDL texture pipeline ", SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_COPY);
slime73@11801
   450
slime73@11802
   451
    MTLSamplerDescriptor *samplerdesc = [[MTLSamplerDescriptor alloc] init];
slime73@11801
   452
slime73@11801
   453
    samplerdesc.minFilter = MTLSamplerMinMagFilterNearest;
slime73@11801
   454
    samplerdesc.magFilter = MTLSamplerMinMagFilterNearest;
slime73@11804
   455
    id<MTLSamplerState> mtlsamplernearest = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc];
slime73@11804
   456
    data.mtlsamplernearest = mtlsamplernearest;
slime73@11801
   457
slime73@11801
   458
    samplerdesc.minFilter = MTLSamplerMinMagFilterLinear;
slime73@11801
   459
    samplerdesc.magFilter = MTLSamplerMinMagFilterLinear;
slime73@11804
   460
    id<MTLSamplerState> mtlsamplerlinear = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc];
slime73@11804
   461
    data.mtlsamplerlinear = mtlsamplerlinear;
slime73@11802
   462
slime73@11789
   463
    static const float clearverts[] = { 0, 0,  0, 3,  3, 0 };
slime73@11804
   464
    id<MTLBuffer> mtlbufclearverts = [data.mtldevice newBufferWithBytes:clearverts length:sizeof(clearverts) options:MTLResourceCPUCacheModeWriteCombined];
slime73@11804
   465
    data.mtlbufclearverts = mtlbufclearverts;
slouken@11735
   466
    data.mtlbufclearverts.label = @"SDL_RenderClear vertices";
slouken@11735
   467
slime73@11799
   468
    float identitytx[16];
slime73@11799
   469
    SDL_memset(identitytx, 0, sizeof(identitytx));
slime73@11799
   470
    identitytx[0] = identitytx[5] = identitytx[10] = identitytx[15] = 1.0f;
slime73@11804
   471
    id<MTLBuffer> mtlbufidentitytransform = [data.mtldevice newBufferWithBytes:identitytx length:sizeof(identitytx) options:0];
slime73@11804
   472
    data.mtlbufidentitytransform = mtlbufidentitytransform;
slime73@11799
   473
    data.mtlbufidentitytransform.label = @"SDL_RenderCopy identity transform";
slime73@11799
   474
slouken@11735
   475
    // !!! FIXME: force more clears here so all the drawables are sane to start, and our static buffers are definitely flushed.
icculus@11729
   476
icculus@11729
   477
    renderer->WindowEvent = METAL_WindowEvent;
icculus@11729
   478
    renderer->GetOutputSize = METAL_GetOutputSize;
slime73@11800
   479
    renderer->SupportsBlendMode = METAL_SupportsBlendMode;
icculus@11729
   480
    renderer->CreateTexture = METAL_CreateTexture;
icculus@11729
   481
    renderer->UpdateTexture = METAL_UpdateTexture;
icculus@11729
   482
    renderer->UpdateTextureYUV = METAL_UpdateTextureYUV;
icculus@11729
   483
    renderer->LockTexture = METAL_LockTexture;
icculus@11729
   484
    renderer->UnlockTexture = METAL_UnlockTexture;
icculus@11729
   485
    renderer->SetRenderTarget = METAL_SetRenderTarget;
icculus@11729
   486
    renderer->UpdateViewport = METAL_UpdateViewport;
icculus@11729
   487
    renderer->UpdateClipRect = METAL_UpdateClipRect;
icculus@11729
   488
    renderer->RenderClear = METAL_RenderClear;
icculus@11729
   489
    renderer->RenderDrawPoints = METAL_RenderDrawPoints;
icculus@11729
   490
    renderer->RenderDrawLines = METAL_RenderDrawLines;
icculus@11729
   491
    renderer->RenderFillRects = METAL_RenderFillRects;
icculus@11729
   492
    renderer->RenderCopy = METAL_RenderCopy;
icculus@11729
   493
    renderer->RenderCopyEx = METAL_RenderCopyEx;
icculus@11729
   494
    renderer->RenderReadPixels = METAL_RenderReadPixels;
icculus@11729
   495
    renderer->RenderPresent = METAL_RenderPresent;
icculus@11729
   496
    renderer->DestroyTexture = METAL_DestroyTexture;
icculus@11729
   497
    renderer->DestroyRenderer = METAL_DestroyRenderer;
slouken@11744
   498
    renderer->GetMetalLayer = METAL_GetMetalLayer;
slouken@11744
   499
    renderer->GetMetalCommandEncoder = METAL_GetMetalCommandEncoder;
icculus@11729
   500
icculus@11729
   501
    renderer->info = METAL_RenderDriver.info;
icculus@11729
   502
    renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
icculus@11729
   503
slime73@11790
   504
#if defined(__MACOSX__) && defined(MAC_OS_X_VERSION_10_13)
slime73@11790
   505
    if (@available(macOS 10.13, *)) {
slime73@11798
   506
        data.mtllayer.displaySyncEnabled = (flags & SDL_RENDERER_PRESENTVSYNC) != 0;
slime73@11790
   507
    } else
slime73@11790
   508
#endif
slime73@11790
   509
    {
slime73@11790
   510
        renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
slime73@11790
   511
    }
icculus@11729
   512
slime73@11804
   513
#if !__has_feature(objc_arc)
slime73@11804
   514
    [mtlcmdqueue release];
slime73@11804
   515
    [mtllibrary release];
slime73@11804
   516
    [samplerdesc release];
slime73@11804
   517
    [mtlsamplernearest release];
slime73@11804
   518
    [mtlsamplerlinear release];
slime73@11804
   519
    [mtlbufclearverts release];
slime73@11804
   520
    [mtlbufidentitytransform release];
slime73@11804
   521
    [view release];
slime73@11804
   522
    [data release];
slime73@11804
   523
#ifdef __MACOSX__
slime73@11804
   524
    [mtldevice release];
slime73@11804
   525
#endif
slime73@11804
   526
#endif
slime73@11804
   527
slouken@11735
   528
    return renderer;
slouken@11735
   529
}
icculus@11729
   530
slime73@11804
   531
static void
slime73@11804
   532
METAL_ActivateRenderer(SDL_Renderer * renderer)
slouken@11735
   533
{
slouken@11735
   534
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@11729
   535
slouken@11735
   536
    if (data.beginScene) {
slouken@11735
   537
        data.beginScene = NO;
slouken@11735
   538
        data.mtlbackbuffer = [data.mtllayer nextDrawable];
slouken@11735
   539
        SDL_assert(data.mtlbackbuffer);
slouken@11735
   540
        data.mtlpassdesc.colorAttachments[0].texture = data.mtlbackbuffer.texture;
slouken@11735
   541
        data.mtlpassdesc.colorAttachments[0].loadAction = MTLLoadActionDontCare;
slouken@11735
   542
        data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
slouken@11735
   543
        data.mtlcmdencoder = [data.mtlcmdbuffer renderCommandEncoderWithDescriptor:data.mtlpassdesc];
icculus@11750
   544
        data.mtlcmdencoder.label = @"SDL metal renderer start of frame";
icculus@11729
   545
slouken@11735
   546
        // Set up our current renderer state for the next frame...
slouken@11735
   547
        METAL_UpdateViewport(renderer);
slouken@11735
   548
        METAL_UpdateClipRect(renderer);
slouken@11735
   549
    }
icculus@11729
   550
}
icculus@11729
   551
icculus@11729
   552
static void
icculus@11729
   553
METAL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
icculus@11729
   554
{
icculus@11729
   555
    if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
icculus@11729
   556
        event->event == SDL_WINDOWEVENT_SHOWN ||
icculus@11729
   557
        event->event == SDL_WINDOWEVENT_HIDDEN) {
icculus@11729
   558
        // !!! FIXME: write me
icculus@11729
   559
    }
icculus@11729
   560
}
icculus@11729
   561
icculus@11729
   562
static int
icculus@11729
   563
METAL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
slouken@11743
   564
{ @autoreleasepool {
slime73@11798
   565
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slime73@11798
   566
    // !!! FIXME: We shouldn't need ActivateRenderer, but drawableSize is 0
slime73@11798
   567
    // in the first frame without it.
slouken@11735
   568
    METAL_ActivateRenderer(renderer);
slime73@11798
   569
    if (w) {
slime73@11798
   570
        *w = (int)data.mtllayer.drawableSize.width;
slime73@11798
   571
    }
slime73@11798
   572
    if (h) {
slime73@11798
   573
        *h = (int)data.mtllayer.drawableSize.height;
slime73@11798
   574
    }
icculus@11729
   575
    return 0;
slouken@11743
   576
}}
icculus@11729
   577
slime73@11800
   578
static SDL_bool
slime73@11800
   579
METAL_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
slime73@11800
   580
{
slime73@11800
   581
    SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode);
slime73@11800
   582
    SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode);
slime73@11800
   583
    SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode);
slime73@11800
   584
    SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode);
slime73@11800
   585
    SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode);
slime73@11800
   586
    SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode);
slime73@11800
   587
slime73@11800
   588
    if (GetBlendFactor(srcColorFactor) == invalidBlendFactor ||
slime73@11800
   589
        GetBlendFactor(srcAlphaFactor) == invalidBlendFactor ||
slime73@11800
   590
        GetBlendOperation(colorOperation) == invalidBlendOperation ||
slime73@11800
   591
        GetBlendFactor(dstColorFactor) == invalidBlendFactor ||
slime73@11800
   592
        GetBlendFactor(dstAlphaFactor) == invalidBlendFactor ||
slime73@11800
   593
        GetBlendOperation(alphaOperation) == invalidBlendOperation) {
slime73@11800
   594
        return SDL_FALSE;
slime73@11800
   595
    }
slime73@11800
   596
    return SDL_TRUE;
slime73@11800
   597
}
slime73@11800
   598
icculus@11729
   599
static int
icculus@11729
   600
METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
slouken@11743
   601
{ @autoreleasepool {
slouken@11732
   602
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@11729
   603
    MTLPixelFormat mtlpixfmt;
icculus@11729
   604
icculus@11729
   605
    switch (texture->format) {
icculus@11729
   606
        case SDL_PIXELFORMAT_ABGR8888: mtlpixfmt = MTLPixelFormatRGBA8Unorm; break;
icculus@11729
   607
        case SDL_PIXELFORMAT_ARGB8888: mtlpixfmt = MTLPixelFormatBGRA8Unorm; break;
icculus@11729
   608
        default: return SDL_SetError("Texture format %s not supported by Metal", SDL_GetPixelFormatName(texture->format));
icculus@11729
   609
    }
icculus@11729
   610
icculus@11729
   611
    MTLTextureDescriptor *mtltexdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:mtlpixfmt
icculus@11729
   612
                                            width:(NSUInteger)texture->w height:(NSUInteger)texture->h mipmapped:NO];
icculus@11750
   613
 
icculus@11750
   614
    if (texture->access == SDL_TEXTUREACCESS_TARGET) {
icculus@11750
   615
        mtltexdesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
icculus@11750
   616
    } else {
icculus@11750
   617
        mtltexdesc.usage = MTLTextureUsageShaderRead;
icculus@11750
   618
    }
icculus@11750
   619
    //mtltexdesc.resourceOptions = MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeManaged;
icculus@11750
   620
    //mtltexdesc.storageMode = MTLStorageModeManaged;
icculus@11750
   621
    
slouken@11732
   622
    id<MTLTexture> mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
icculus@11729
   623
    if (mtltexture == nil) {
icculus@11729
   624
        return SDL_SetError("Texture allocation failed");
icculus@11729
   625
    }
icculus@11729
   626
slouken@11753
   627
    METAL_TextureData *texturedata = [[METAL_TextureData alloc] init];
slouken@11753
   628
    const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
slouken@11753
   629
    if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
slime73@11801
   630
        texturedata.mtlsampler = data.mtlsamplernearest;
slouken@11753
   631
    } else {
slime73@11801
   632
        texturedata.mtlsampler = data.mtlsamplerlinear;
slouken@11753
   633
    }
slouken@11753
   634
    texturedata.mtltexture = mtltexture;
slouken@11753
   635
slouken@11753
   636
    texture->driverdata = (void*)CFBridgingRetain(texturedata);
icculus@11729
   637
slime73@11799
   638
#if !__has_feature(objc_arc)
slime73@11804
   639
    [texturedata release];
slime73@11799
   640
    [mtltexture release];
slime73@11799
   641
#endif
slime73@11799
   642
icculus@11729
   643
    return 0;
slouken@11743
   644
}}
icculus@11729
   645
icculus@11729
   646
static int
icculus@11729
   647
METAL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   648
                 const SDL_Rect * rect, const void *pixels, int pitch)
slouken@11743
   649
{ @autoreleasepool {
icculus@11729
   650
    // !!! FIXME: this is a synchronous call; it doesn't return until data is uploaded in some form.
icculus@11729
   651
    // !!! 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
   652
    // !!! 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
   653
    // !!! FIXME:  stuff way ahead of time and/or using it immediately after upload?
slouken@11753
   654
    id<MTLTexture> mtltexture = ((__bridge METAL_TextureData *)texture->driverdata).mtltexture;
icculus@11729
   655
    [mtltexture replaceRegion:MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h) mipmapLevel:0 withBytes:pixels bytesPerRow:pitch];
icculus@11729
   656
    return 0;
slouken@11743
   657
}}
icculus@11729
   658
icculus@11729
   659
static int
icculus@11729
   660
METAL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   661
                    const SDL_Rect * rect,
icculus@11729
   662
                    const Uint8 *Yplane, int Ypitch,
icculus@11729
   663
                    const Uint8 *Uplane, int Upitch,
icculus@11729
   664
                    const Uint8 *Vplane, int Vpitch)
icculus@11729
   665
{
icculus@11729
   666
    return SDL_Unsupported();  // !!! FIXME
icculus@11729
   667
}
icculus@11729
   668
icculus@11729
   669
static int
icculus@11729
   670
METAL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   671
               const SDL_Rect * rect, void **pixels, int *pitch)
icculus@11729
   672
{
icculus@11729
   673
    return SDL_Unsupported();   // !!! FIXME: write me
icculus@11729
   674
}
icculus@11729
   675
icculus@11729
   676
static void
icculus@11729
   677
METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
icculus@11729
   678
{
icculus@11729
   679
    // !!! FIXME: write me
icculus@11729
   680
}
icculus@11729
   681
icculus@11729
   682
static int
icculus@11729
   683
METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
slouken@11743
   684
{ @autoreleasepool {
slouken@11735
   685
    METAL_ActivateRenderer(renderer);
slouken@11732
   686
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@11750
   687
icculus@11750
   688
    // commit the current command buffer, so that any work on a render target
icculus@11750
   689
    //  will be available to the next one we're about to queue up.
icculus@11750
   690
    [data.mtlcmdencoder endEncoding];
icculus@11750
   691
    [data.mtlcmdbuffer commit];
icculus@11750
   692
slouken@11753
   693
    id<MTLTexture> mtltexture = texture ? ((__bridge METAL_TextureData *)texture->driverdata).mtltexture : data.mtlbackbuffer.texture;
slouken@11732
   694
    data.mtlpassdesc.colorAttachments[0].texture = mtltexture;
icculus@11750
   695
    // !!! FIXME: this can be MTLLoadActionDontCare for textures (not the backbuffer) if SDL doesn't guarantee the texture contents should survive.
icculus@11750
   696
    data.mtlpassdesc.colorAttachments[0].loadAction = MTLLoadActionLoad;
icculus@11750
   697
    data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
icculus@11750
   698
    data.mtlcmdencoder = [data.mtlcmdbuffer renderCommandEncoderWithDescriptor:data.mtlpassdesc];
icculus@11750
   699
    data.mtlcmdencoder.label = texture ? @"SDL metal renderer render texture" : @"SDL metal renderer backbuffer";
icculus@11750
   700
icculus@11750
   701
    // The higher level will reset the viewport and scissor after this call returns.
icculus@11750
   702
icculus@11729
   703
    return 0;
slouken@11743
   704
}}
icculus@11729
   705
icculus@11729
   706
static int
slime73@11789
   707
METAL_SetOrthographicProjection(SDL_Renderer *renderer, int w, int h)
slime73@11789
   708
{ @autoreleasepool {
slime73@11789
   709
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slime73@11789
   710
    float projection[4][4];
slime73@11789
   711
slime73@11789
   712
    if (!w || !h) {
slime73@11789
   713
        return 0;
slime73@11789
   714
    }
slime73@11789
   715
slime73@11789
   716
    /* Prepare an orthographic projection */
slime73@11789
   717
    projection[0][0] = 2.0f / w;
slime73@11789
   718
    projection[0][1] = 0.0f;
slime73@11789
   719
    projection[0][2] = 0.0f;
slime73@11789
   720
    projection[0][3] = 0.0f;
slime73@11789
   721
    projection[1][0] = 0.0f;
slime73@11789
   722
    projection[1][1] = -2.0f / h;
slime73@11789
   723
    projection[1][2] = 0.0f;
slime73@11789
   724
    projection[1][3] = 0.0f;
slime73@11789
   725
    projection[2][0] = 0.0f;
slime73@11789
   726
    projection[2][1] = 0.0f;
slime73@11789
   727
    projection[2][2] = 0.0f;
slime73@11789
   728
    projection[2][3] = 0.0f;
slime73@11789
   729
    projection[3][0] = -1.0f;
slime73@11789
   730
    projection[3][1] = 1.0f;
slime73@11789
   731
    projection[3][2] = 0.0f;
slime73@11789
   732
    projection[3][3] = 1.0f;
slime73@11789
   733
slime73@11789
   734
    // !!! FIXME: This should be in a buffer...
slime73@11789
   735
    [data.mtlcmdencoder setVertexBytes:projection length:sizeof(float)*16 atIndex:2];
slime73@11789
   736
    return 0;
slime73@11789
   737
}}
slime73@11789
   738
slime73@11789
   739
static int
icculus@11729
   740
METAL_UpdateViewport(SDL_Renderer * renderer)
slouken@11743
   741
{ @autoreleasepool {
slouken@11735
   742
    METAL_ActivateRenderer(renderer);
slouken@11732
   743
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slouken@11735
   744
    MTLViewport viewport;
slouken@11735
   745
    viewport.originX = renderer->viewport.x;
slouken@11735
   746
    viewport.originY = renderer->viewport.y;
slouken@11735
   747
    viewport.width = renderer->viewport.w;
slouken@11735
   748
    viewport.height = renderer->viewport.h;
slouken@11735
   749
    viewport.znear = 0.0;
slouken@11735
   750
    viewport.zfar = 1.0;
slouken@11735
   751
    [data.mtlcmdencoder setViewport:viewport];
slime73@11789
   752
    METAL_SetOrthographicProjection(renderer, renderer->viewport.w, renderer->viewport.h);
icculus@11729
   753
    return 0;
slouken@11743
   754
}}
icculus@11729
   755
icculus@11729
   756
static int
icculus@11729
   757
METAL_UpdateClipRect(SDL_Renderer * renderer)
slouken@11743
   758
{ @autoreleasepool {
slouken@11735
   759
    METAL_ActivateRenderer(renderer);
slouken@11732
   760
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slouken@11735
   761
    MTLScissorRect mtlrect;
slouken@11743
   762
    // !!! FIXME: should this care about the viewport?
slouken@11735
   763
    if (renderer->clipping_enabled) {
slouken@11735
   764
        const SDL_Rect *rect = &renderer->clip_rect;
slouken@11735
   765
        mtlrect.x = renderer->viewport.x + rect->x;
slouken@11735
   766
        mtlrect.y = renderer->viewport.x + rect->y;
slouken@11735
   767
        mtlrect.width = rect->w;
slouken@11735
   768
        mtlrect.height = rect->h;
slouken@11735
   769
    } else {
slouken@11735
   770
        mtlrect.x = renderer->viewport.x;
slouken@11735
   771
        mtlrect.y = renderer->viewport.y;
slouken@11735
   772
        mtlrect.width = renderer->viewport.w;
slouken@11735
   773
        mtlrect.height = renderer->viewport.h;
slouken@11735
   774
    }
slouken@11735
   775
    if (mtlrect.width > 0 && mtlrect.height > 0) {
slouken@11735
   776
        [data.mtlcmdencoder setScissorRect:mtlrect];
icculus@11729
   777
    }
icculus@11729
   778
    return 0;
slouken@11743
   779
}}
icculus@11729
   780
icculus@11729
   781
static int
icculus@11729
   782
METAL_RenderClear(SDL_Renderer * renderer)
slouken@11743
   783
{ @autoreleasepool {
icculus@11729
   784
    // We could dump the command buffer and force a clear on a new one, but this will respect the scissor state.
slouken@11735
   785
    METAL_ActivateRenderer(renderer);
slouken@11732
   786
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@11729
   787
icculus@11729
   788
    // !!! FIXME: render color should live in a dedicated uniform buffer.
icculus@11729
   789
    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
   790
icculus@11729
   791
    MTLViewport viewport;  // RenderClear ignores the viewport state, though, so reset that.
icculus@11729
   792
    viewport.originX = viewport.originY = 0.0;
icculus@11750
   793
    viewport.width = data.mtlpassdesc.colorAttachments[0].texture.width;
icculus@11750
   794
    viewport.height = data.mtlpassdesc.colorAttachments[0].texture.height;
icculus@11729
   795
    viewport.znear = 0.0;
icculus@11729
   796
    viewport.zfar = 1.0;
icculus@11729
   797
slime73@11788
   798
    // Draw a simple filled fullscreen triangle now.
slime73@11789
   799
    METAL_SetOrthographicProjection(renderer, 1, 1);
slouken@11732
   800
    [data.mtlcmdencoder setViewport:viewport];
slime73@11800
   801
    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.mtlpipelineprims, SDL_BLENDMODE_NONE)];
slouken@11732
   802
    [data.mtlcmdencoder setVertexBuffer:data.mtlbufclearverts offset:0 atIndex:0];
slouken@11732
   803
    [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
slime73@11788
   804
    [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
icculus@11729
   805
icculus@11729
   806
    // reset the viewport for the rest of our usual drawing work...
icculus@11729
   807
    viewport.originX = renderer->viewport.x;
icculus@11729
   808
    viewport.originY = renderer->viewport.y;
icculus@11729
   809
    viewport.width = renderer->viewport.w;
icculus@11729
   810
    viewport.height = renderer->viewport.h;
icculus@11729
   811
    viewport.znear = 0.0;
icculus@11729
   812
    viewport.zfar = 1.0;
slouken@11732
   813
    [data.mtlcmdencoder setViewport:viewport];
slime73@11789
   814
    METAL_SetOrthographicProjection(renderer, renderer->viewport.w, renderer->viewport.h);
icculus@11729
   815
icculus@11729
   816
    return 0;
slouken@11743
   817
}}
icculus@11729
   818
slouken@11806
   819
// adjust pixel center for x and y coordinates
slouken@11806
   820
static inline float
slouken@11806
   821
adjustx(const float val)
slouken@11806
   822
{
slouken@11806
   823
	return (val + 0.5f);
slouken@11806
   824
}
slouken@11806
   825
static inline float
slouken@11806
   826
adjusty(const float val)
slouken@11806
   827
{
slouken@11807
   828
	return (val + 0.5f);
slouken@11806
   829
}
slouken@11806
   830
slouken@11755
   831
// normalize a value from 0.0f to len into 0.0f to 1.0f.
slouken@11755
   832
static inline float
slouken@11755
   833
normtex(const float _val, const float len)
slouken@11755
   834
{
slouken@11755
   835
    const float val = (_val < 0.0f) ? 0.0f : (_val > len) ? len : _val;
slouken@11755
   836
    return ((val + 0.5f) / len);
slouken@11755
   837
}
slouken@11755
   838
icculus@11729
   839
static int
icculus@11729
   840
DrawVerts(SDL_Renderer * renderer, const SDL_FPoint * points, int count,
icculus@11729
   841
          const MTLPrimitiveType primtype)
slouken@11743
   842
{ @autoreleasepool {
slouken@11735
   843
    METAL_ActivateRenderer(renderer);
slouken@11735
   844
slouken@11806
   845
    const size_t vertlen = (sizeof (float) * 2) * count;
slouken@11806
   846
    float *verts = SDL_malloc(vertlen);
slouken@11806
   847
    if (!verts) {
slouken@11806
   848
        return SDL_OutOfMemory();
slouken@11806
   849
    }
slouken@11806
   850
slouken@11732
   851
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@11729
   852
icculus@11729
   853
    // !!! FIXME: render color should live in a dedicated uniform buffer.
icculus@11729
   854
    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
   855
slime73@11800
   856
    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.mtlpipelineprims, renderer->blendMode)];
slouken@11732
   857
    [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
slouken@11806
   858
slouken@11806
   859
    float *ptr = verts;
slouken@11806
   860
    for (int i = 0; i < count; i++, points++) {
slouken@11806
   861
        *ptr = adjustx(points->x); ptr++;
slouken@11806
   862
        *ptr = adjusty(points->y); ptr++;
slouken@11806
   863
    }
slouken@11806
   864
slouken@11806
   865
    [data.mtlcmdencoder setVertexBytes:verts length:vertlen atIndex:0];
slouken@11732
   866
    [data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];
slouken@11806
   867
slouken@11806
   868
    SDL_free(verts);
icculus@11729
   869
    return 0;
slouken@11743
   870
}}
icculus@11729
   871
icculus@11729
   872
static int
icculus@11729
   873
METAL_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points, int count)
icculus@11729
   874
{
icculus@11729
   875
    return DrawVerts(renderer, points, count, MTLPrimitiveTypePoint);
icculus@11729
   876
}
icculus@11729
   877
icculus@11729
   878
static int
icculus@11729
   879
METAL_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points, int count)
icculus@11729
   880
{
icculus@11729
   881
    return DrawVerts(renderer, points, count, MTLPrimitiveTypeLineStrip);
icculus@11729
   882
}
icculus@11729
   883
icculus@11729
   884
static int
icculus@11729
   885
METAL_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count)
slouken@11743
   886
{ @autoreleasepool {
slouken@11735
   887
    METAL_ActivateRenderer(renderer);
slouken@11732
   888
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@11729
   889
icculus@11729
   890
    // !!! FIXME: render color should live in a dedicated uniform buffer.
icculus@11729
   891
    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
   892
slime73@11800
   893
    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.mtlpipelineprims, renderer->blendMode)];
slouken@11732
   894
    [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
icculus@11729
   895
icculus@11729
   896
    for (int i = 0; i < count; i++, rects++) {
icculus@11729
   897
        if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) continue;
icculus@11729
   898
icculus@11729
   899
        const float verts[] = {
slouken@11806
   900
            adjustx(rects->x), adjusty(rects->y + rects->h),
slouken@11806
   901
            adjustx(rects->x), adjusty(rects->y),
slouken@11806
   902
            adjustx(rects->x + rects->w), adjusty(rects->y + rects->h),
slouken@11806
   903
            adjustx(rects->x + rects->w), adjusty(rects->y)
icculus@11729
   904
        };
icculus@11729
   905
slouken@11732
   906
        [data.mtlcmdencoder setVertexBytes:verts length:sizeof(verts) atIndex:0];
slime73@11788
   907
        [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
icculus@11729
   908
    }
icculus@11729
   909
icculus@11729
   910
    return 0;
slouken@11743
   911
}}
icculus@11729
   912
icculus@11729
   913
static int
icculus@11729
   914
METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   915
              const SDL_Rect * srcrect, const SDL_FRect * dstrect)
slouken@11743
   916
{ @autoreleasepool {
slouken@11735
   917
    METAL_ActivateRenderer(renderer);
slouken@11732
   918
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slouken@11753
   919
    METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
slouken@11753
   920
    const float texw = (float) texturedata.mtltexture.width;
slouken@11753
   921
    const float texh = (float) texturedata.mtltexture.height;
icculus@11729
   922
icculus@11729
   923
    const float xy[] = {
slouken@11806
   924
        adjustx(dstrect->x), adjusty(dstrect->y + dstrect->h),
slouken@11806
   925
        adjustx(dstrect->x), adjusty(dstrect->y),
slouken@11806
   926
        adjustx(dstrect->x + dstrect->w), adjusty(dstrect->y + dstrect->h),
slouken@11806
   927
        adjustx(dstrect->x + dstrect->w), adjusty(dstrect->y)
icculus@11729
   928
    };
icculus@11729
   929
icculus@11729
   930
    const float uv[] = {
slouken@11755
   931
        normtex(srcrect->x, texw), normtex(srcrect->y + srcrect->h, texh),
slouken@11755
   932
        normtex(srcrect->x, texw), normtex(srcrect->y, texh),
slime73@11788
   933
        normtex(srcrect->x + srcrect->w, texw), normtex(srcrect->y + srcrect->h, texh),
slime73@11788
   934
        normtex(srcrect->x + srcrect->w, texw), normtex(srcrect->y, texh)
icculus@11729
   935
    };
icculus@11729
   936
icculus@11729
   937
    float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
icculus@11729
   938
    if (texture->modMode) {
icculus@11729
   939
        color[0] = ((float)texture->r) / 255.0f;
icculus@11729
   940
        color[1] = ((float)texture->g) / 255.0f;
icculus@11729
   941
        color[2] = ((float)texture->b) / 255.0f;
icculus@11729
   942
        color[3] = ((float)texture->a) / 255.0f;
icculus@11729
   943
    }
icculus@11729
   944
slime73@11801
   945
    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.mtlpipelinecopy, texture->blendMode)];
slime73@11799
   946
    [data.mtlcmdencoder setVertexBytes:xy length:sizeof(xy) atIndex:0];
slime73@11799
   947
    [data.mtlcmdencoder setVertexBytes:uv length:sizeof(uv) atIndex:1];
slime73@11799
   948
    [data.mtlcmdencoder setVertexBuffer:data.mtlbufidentitytransform offset:0 atIndex:3];
slouken@11732
   949
    [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
slouken@11753
   950
    [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0];
slime73@11801
   951
    [data.mtlcmdencoder setFragmentSamplerState:texturedata.mtlsampler atIndex:0];
slime73@11788
   952
    [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
icculus@11729
   953
icculus@11729
   954
    return 0;
slouken@11743
   955
}}
icculus@11729
   956
icculus@11729
   957
static int
icculus@11729
   958
METAL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   959
              const SDL_Rect * srcrect, const SDL_FRect * dstrect,
icculus@11729
   960
              const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
slime73@11799
   961
{ @autoreleasepool {
slime73@11799
   962
    METAL_ActivateRenderer(renderer);
slime73@11799
   963
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slime73@11799
   964
    METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
slime73@11799
   965
    const float texw = (float) texturedata.mtltexture.width;
slime73@11799
   966
    const float texh = (float) texturedata.mtltexture.height;
slime73@11799
   967
    float transform[16];
slime73@11799
   968
    float minu, maxu, minv, maxv;
slime73@11799
   969
slime73@11799
   970
    minu = normtex(srcrect->x, texw);
slime73@11799
   971
    maxu = normtex(srcrect->x + srcrect->w, texw);
slime73@11799
   972
    minv = normtex(srcrect->y, texh);
slime73@11799
   973
    maxv = normtex(srcrect->y + srcrect->h, texh);
slime73@11799
   974
slime73@11799
   975
    if (flip & SDL_FLIP_HORIZONTAL) {
slime73@11799
   976
        float tmp = maxu;
slime73@11799
   977
        maxu = minu;
slime73@11799
   978
        minu = tmp;
slime73@11799
   979
    }
slime73@11799
   980
    if (flip & SDL_FLIP_VERTICAL) {
slime73@11799
   981
        float tmp = maxv;
slime73@11799
   982
        maxv = minv;
slime73@11799
   983
        minv = tmp;
slime73@11799
   984
    }
slime73@11799
   985
slime73@11799
   986
    const float uv[] = {
slime73@11799
   987
        minu, maxv,
slime73@11799
   988
        minu, minv,
slime73@11799
   989
        maxu, maxv,
slime73@11799
   990
        maxu, minv
slime73@11799
   991
    };
slime73@11799
   992
slime73@11799
   993
    const float xy[] = {
slouken@11806
   994
        adjustx(-center->x), adjusty(dstrect->h - center->y),
slouken@11806
   995
        adjustx(-center->x), adjusty(-center->y),
slouken@11806
   996
        adjustx(dstrect->w - center->x), adjusty(dstrect->h - center->y),
slouken@11806
   997
        adjustx(dstrect->w - center->x), adjusty(-center->y)
slime73@11799
   998
    };
slime73@11799
   999
slime73@11799
  1000
    {
slime73@11799
  1001
        float rads = (float)(M_PI * (float) angle / 180.0f);
slime73@11799
  1002
        float c = cosf(rads), s = sinf(rads);
slime73@11799
  1003
        SDL_memset(transform, 0, sizeof(transform));
slime73@11799
  1004
slime73@11799
  1005
        // matrix multiplication carried out on paper:
slime73@11799
  1006
        // |1     x+c| |c -s    |
slime73@11799
  1007
        // |  1   y+c| |s  c    |
slime73@11799
  1008
        // |    1    | |     1  |
slime73@11799
  1009
        // |        1| |       1|
slime73@11799
  1010
        //     move      rotate
slime73@11799
  1011
        transform[10] = transform[15] = 1.0f;
slime73@11799
  1012
        transform[0]  = c;
slime73@11799
  1013
        transform[1]  = s;
slime73@11799
  1014
        transform[4]  = -s;
slime73@11799
  1015
        transform[5]  = c;
slime73@11799
  1016
        transform[12] = dstrect->x + center->x;
slime73@11799
  1017
        transform[13] = dstrect->y + center->y;
slime73@11799
  1018
    }
slime73@11799
  1019
slime73@11799
  1020
    float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
slime73@11799
  1021
    if (texture->modMode) {
slime73@11799
  1022
        color[0] = ((float)texture->r) / 255.0f;
slime73@11799
  1023
        color[1] = ((float)texture->g) / 255.0f;
slime73@11799
  1024
        color[2] = ((float)texture->b) / 255.0f;
slime73@11799
  1025
        color[3] = ((float)texture->a) / 255.0f;
slime73@11799
  1026
    }
slime73@11799
  1027
slime73@11801
  1028
    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.mtlpipelinecopy, texture->blendMode)];
slime73@11799
  1029
    [data.mtlcmdencoder setVertexBytes:xy length:sizeof(xy) atIndex:0];
slime73@11799
  1030
    [data.mtlcmdencoder setVertexBytes:uv length:sizeof(uv) atIndex:1];
slime73@11799
  1031
    [data.mtlcmdencoder setVertexBytes:transform length:sizeof(transform) atIndex:3];
slime73@11799
  1032
    [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
slime73@11799
  1033
    [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0];
slime73@11801
  1034
    [data.mtlcmdencoder setFragmentSamplerState:texturedata.mtlsampler atIndex:0];
slime73@11799
  1035
    [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
slime73@11799
  1036
slime73@11799
  1037
    return 0;
slime73@11799
  1038
}}
icculus@11729
  1039
icculus@11729
  1040
static int
icculus@11729
  1041
METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
icculus@11729
  1042
                    Uint32 pixel_format, void * pixels, int pitch)
slouken@11743
  1043
{ @autoreleasepool {
slouken@11735
  1044
    METAL_ActivateRenderer(renderer);
icculus@11749
  1045
    // !!! FIXME: this probably needs to commit the current command buffer, and probably waitUntilCompleted
slouken@11732
  1046
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slime73@11804
  1047
    id<MTLTexture> mtltexture = data.mtlpassdesc.colorAttachments[0].texture;
slime73@11800
  1048
    MTLRegion mtlregion = MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h);
icculus@11729
  1049
icculus@11729
  1050
    // we only do BGRA8 or RGBA8 at the moment, so 4 will do.
icculus@11729
  1051
    const int temp_pitch = rect->w * 4;
icculus@11729
  1052
    void *temp_pixels = SDL_malloc(temp_pitch * rect->h);
icculus@11729
  1053
    if (!temp_pixels) {
icculus@11729
  1054
        return SDL_OutOfMemory();
icculus@11729
  1055
    }
icculus@11729
  1056
icculus@11729
  1057
    [mtltexture getBytes:temp_pixels bytesPerRow:temp_pitch fromRegion:mtlregion mipmapLevel:0];
icculus@11729
  1058
icculus@11729
  1059
    const Uint32 temp_format = (mtltexture.pixelFormat == MTLPixelFormatBGRA8Unorm) ? SDL_PIXELFORMAT_ARGB8888 : SDL_PIXELFORMAT_ABGR8888;
icculus@11729
  1060
    const int status = SDL_ConvertPixels(rect->w, rect->h, temp_format, temp_pixels, temp_pitch, pixel_format, pixels, pitch);
icculus@11729
  1061
    SDL_free(temp_pixels);
icculus@11729
  1062
    return status;
slouken@11743
  1063
}}
icculus@11729
  1064
icculus@11729
  1065
static void
icculus@11729
  1066
METAL_RenderPresent(SDL_Renderer * renderer)
slouken@11743
  1067
{ @autoreleasepool {
slouken@11735
  1068
    METAL_ActivateRenderer(renderer);
slouken@11732
  1069
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@11729
  1070
slouken@11732
  1071
    [data.mtlcmdencoder endEncoding];
slouken@11741
  1072
    [data.mtlcmdbuffer presentDrawable:data.mtlbackbuffer];
slouken@11732
  1073
    [data.mtlcmdbuffer commit];
slouken@11741
  1074
    data.mtlcmdencoder = nil;
slouken@11741
  1075
    data.mtlcmdbuffer = nil;
slouken@11741
  1076
    data.mtlbackbuffer = nil;
slouken@11735
  1077
    data.beginScene = YES;
slouken@11743
  1078
}}
icculus@11729
  1079
icculus@11729
  1080
static void
icculus@11729
  1081
METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
slouken@11743
  1082
{ @autoreleasepool {
slime73@11804
  1083
    CFBridgingRelease(texture->driverdata);
icculus@11729
  1084
    texture->driverdata = NULL;
slouken@11743
  1085
}}
icculus@11729
  1086
icculus@11729
  1087
static void
icculus@11729
  1088
METAL_DestroyRenderer(SDL_Renderer * renderer)
slouken@11743
  1089
{ @autoreleasepool {
slouken@11732
  1090
    if (renderer->driverdata) {
slouken@11732
  1091
        METAL_RenderData *data = CFBridgingRelease(renderer->driverdata);
icculus@11729
  1092
slouken@11741
  1093
        if (data.mtlcmdencoder != nil) {
slouken@11741
  1094
            [data.mtlcmdencoder endEncoding];
slouken@11741
  1095
        }
icculus@11746
  1096
slime73@11800
  1097
        DestroyPipelineCache(data.mtlpipelineprims);
slime73@11801
  1098
        DestroyPipelineCache(data.mtlpipelinecopy);
icculus@11729
  1099
    }
icculus@11746
  1100
icculus@11729
  1101
    SDL_free(renderer);
slouken@11743
  1102
}}
icculus@11729
  1103
slime73@11804
  1104
static void *
slime73@11804
  1105
METAL_GetMetalLayer(SDL_Renderer * renderer)
slouken@11744
  1106
{ @autoreleasepool {
slouken@11744
  1107
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slouken@11744
  1108
    return (__bridge void*)data.mtllayer;
slouken@11744
  1109
}}
slouken@11744
  1110
slime73@11804
  1111
static void *
slime73@11804
  1112
METAL_GetMetalCommandEncoder(SDL_Renderer * renderer)
slouken@11744
  1113
{ @autoreleasepool {
slouken@11744
  1114
    METAL_ActivateRenderer(renderer);
slouken@11744
  1115
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slouken@11744
  1116
    return (__bridge void*)data.mtlcmdencoder;
slouken@11744
  1117
}}
slouken@11744
  1118
icculus@11729
  1119
#endif /* SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED */
icculus@11729
  1120
icculus@11729
  1121
/* vi: set ts=4 sw=4 expandtab: */