src/render/metal/SDL_render_metal.m
author Ryan C. Gordon <icculus@icculus.org>
Thu, 06 Sep 2018 00:56:13 -0400
changeset 12179 c06be57c5a01
parent 12169 be34b836f738
child 12213 e4f5271ec8cc
child 12320 758d4e1222a7
permissions -rw-r--r--
metal: Make sure layer drawableSize is adjusted on resize.

Fixes Bugzilla #4250.
icculus@11729
     1
/*
icculus@11729
     2
  Simple DirectMedia Layer
slouken@11811
     3
  Copyright (C) 1997-2018 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@11821
    36
#include <Availability.h>
slime73@11798
    37
#import <Metal/Metal.h>
slime73@11798
    38
#import <QuartzCore/CAMetalLayer.h>
icculus@11729
    39
slouken@11730
    40
/* Regenerate these with build-metal-shaders.sh */
slouken@11730
    41
#ifdef __MACOSX__
slouken@11730
    42
#include "SDL_shaders_metal_osx.h"
slouken@11730
    43
#else
slouken@11730
    44
#include "SDL_shaders_metal_ios.h"
slouken@11730
    45
#endif
icculus@11729
    46
icculus@11729
    47
/* Apple Metal renderer implementation */
icculus@11729
    48
icculus@11729
    49
static SDL_Renderer *METAL_CreateRenderer(SDL_Window * window, Uint32 flags);
icculus@11729
    50
static void METAL_WindowEvent(SDL_Renderer * renderer,
icculus@11729
    51
                           const SDL_WindowEvent *event);
icculus@11729
    52
static int METAL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h);
slime73@11800
    53
static SDL_bool METAL_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode);
icculus@11729
    54
static int METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
icculus@11729
    55
static int METAL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    56
                            const SDL_Rect * rect, const void *pixels,
icculus@11729
    57
                            int pitch);
icculus@11729
    58
static int METAL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    59
                               const SDL_Rect * rect,
icculus@11729
    60
                               const Uint8 *Yplane, int Ypitch,
icculus@11729
    61
                               const Uint8 *Uplane, int Upitch,
icculus@11729
    62
                               const Uint8 *Vplane, int Vpitch);
icculus@11729
    63
static int METAL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    64
                          const SDL_Rect * rect, void **pixels, int *pitch);
icculus@11729
    65
static void METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
icculus@11729
    66
static int METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
icculus@11729
    67
static int METAL_UpdateViewport(SDL_Renderer * renderer);
icculus@11729
    68
static int METAL_UpdateClipRect(SDL_Renderer * renderer);
icculus@11729
    69
static int METAL_RenderClear(SDL_Renderer * renderer);
icculus@11729
    70
static int METAL_RenderDrawPoints(SDL_Renderer * renderer,
icculus@11729
    71
                               const SDL_FPoint * points, int count);
icculus@11729
    72
static int METAL_RenderDrawLines(SDL_Renderer * renderer,
icculus@11729
    73
                              const SDL_FPoint * points, int count);
icculus@11729
    74
static int METAL_RenderFillRects(SDL_Renderer * renderer,
icculus@11729
    75
                              const SDL_FRect * rects, int count);
icculus@11729
    76
static int METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    77
                         const SDL_Rect * srcrect, const SDL_FRect * dstrect);
icculus@11729
    78
static int METAL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
    79
                         const SDL_Rect * srcrect, const SDL_FRect * dstrect,
icculus@11729
    80
                         const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
icculus@11729
    81
static int METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
icculus@11729
    82
                               Uint32 pixel_format, void * pixels, int pitch);
icculus@11729
    83
static void METAL_RenderPresent(SDL_Renderer * renderer);
icculus@11729
    84
static void METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture);
icculus@11729
    85
static void METAL_DestroyRenderer(SDL_Renderer * renderer);
slouken@11744
    86
static void *METAL_GetMetalLayer(SDL_Renderer * renderer);
slouken@11744
    87
static void *METAL_GetMetalCommandEncoder(SDL_Renderer * renderer);
icculus@11729
    88
icculus@11729
    89
SDL_RenderDriver METAL_RenderDriver = {
icculus@11729
    90
    METAL_CreateRenderer,
icculus@11729
    91
    {
slime73@11821
    92
        "metal",
slime73@11821
    93
        (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE),
slime73@11821
    94
        6,
slime73@11821
    95
        {
slime73@11821
    96
            SDL_PIXELFORMAT_ARGB8888,
slime73@11821
    97
            SDL_PIXELFORMAT_ABGR8888,
slime73@11821
    98
            SDL_PIXELFORMAT_YV12,
slime73@11821
    99
            SDL_PIXELFORMAT_IYUV,
slime73@11821
   100
            SDL_PIXELFORMAT_NV12,
slime73@11821
   101
            SDL_PIXELFORMAT_NV21
slime73@11821
   102
        },
slime73@11821
   103
    0, 0,
slouken@11751
   104
    }
icculus@11729
   105
};
icculus@11729
   106
slime73@11810
   107
/* macOS requires constants in a buffer to have a 256 byte alignment. */
slime73@11810
   108
#ifdef __MACOSX__
slime73@11810
   109
#define CONSTANT_ALIGN 256
slime73@11810
   110
#else
slime73@11810
   111
#define CONSTANT_ALIGN 4
slime73@11810
   112
#endif
slime73@11810
   113
slime73@11810
   114
#define ALIGN_CONSTANTS(size) ((size + CONSTANT_ALIGN - 1) & (~(CONSTANT_ALIGN - 1)))
slime73@11810
   115
slime73@11810
   116
static const size_t CONSTANTS_OFFSET_IDENTITY = 0;
slime73@11810
   117
static const size_t CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM = ALIGN_CONSTANTS(CONSTANTS_OFFSET_IDENTITY + sizeof(float) * 16);
slime73@11819
   118
static const size_t CONSTANTS_OFFSET_DECODE_JPEG = ALIGN_CONSTANTS(CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM + sizeof(float) * 16);
slime73@11819
   119
static const size_t CONSTANTS_OFFSET_DECODE_BT601 = ALIGN_CONSTANTS(CONSTANTS_OFFSET_DECODE_JPEG + sizeof(float) * 4 * 4);
slime73@11819
   120
static const size_t CONSTANTS_OFFSET_DECODE_BT709 = ALIGN_CONSTANTS(CONSTANTS_OFFSET_DECODE_BT601 + sizeof(float) * 4 * 4);
slime73@11819
   121
static const size_t CONSTANTS_OFFSET_CLEAR_VERTS = ALIGN_CONSTANTS(CONSTANTS_OFFSET_DECODE_BT709 + sizeof(float) * 4 * 4);
slime73@11810
   122
static const size_t CONSTANTS_LENGTH = CONSTANTS_OFFSET_CLEAR_VERTS + sizeof(float) * 6;
slime73@11810
   123
slime73@11800
   124
typedef enum SDL_MetalVertexFunction
slime73@11800
   125
{
slime73@11800
   126
    SDL_METAL_VERTEX_SOLID,
slime73@11800
   127
    SDL_METAL_VERTEX_COPY,
slime73@11800
   128
} SDL_MetalVertexFunction;
slime73@11800
   129
slime73@11800
   130
typedef enum SDL_MetalFragmentFunction
slime73@11800
   131
{
slime73@11819
   132
    SDL_METAL_FRAGMENT_SOLID = 0,
slime73@11801
   133
    SDL_METAL_FRAGMENT_COPY,
slime73@11819
   134
    SDL_METAL_FRAGMENT_YUV,
slime73@11819
   135
    SDL_METAL_FRAGMENT_NV12,
slime73@11819
   136
    SDL_METAL_FRAGMENT_NV21,
slime73@11819
   137
    SDL_METAL_FRAGMENT_COUNT,
slime73@11800
   138
} SDL_MetalFragmentFunction;
slime73@11800
   139
slime73@11800
   140
typedef struct METAL_PipelineState
slime73@11800
   141
{
slime73@11800
   142
    SDL_BlendMode blendMode;
slime73@11800
   143
    void *pipe;
slime73@11800
   144
} METAL_PipelineState;
slime73@11800
   145
slime73@11800
   146
typedef struct METAL_PipelineCache
slime73@11800
   147
{
slime73@11800
   148
    METAL_PipelineState *states;
slime73@11800
   149
    int count;
slime73@11800
   150
    SDL_MetalVertexFunction vertexFunction;
slime73@11800
   151
    SDL_MetalFragmentFunction fragmentFunction;
slime73@11820
   152
    MTLPixelFormat renderTargetFormat;
slime73@11800
   153
    const char *label;
slime73@11800
   154
} METAL_PipelineCache;
slime73@11800
   155
slime73@11819
   156
/* Each shader combination used by drawing functions has a separate pipeline
slime73@11820
   157
 * cache, and we have a separate list of caches for each render target pixel
slime73@11820
   158
 * format. This is more efficient than iterating over a global cache to find
slime73@11820
   159
 * the pipeline based on the specified shader combination and RT pixel format,
slime73@11820
   160
 * since we know what the RT pixel format is when we set the render target, and
slime73@11820
   161
 * we know what the shader combination is inside each drawing function's code. */
slime73@11819
   162
typedef struct METAL_ShaderPipelines
slime73@11819
   163
{
slime73@11820
   164
    MTLPixelFormat renderTargetFormat;
slime73@11819
   165
    METAL_PipelineCache caches[SDL_METAL_FRAGMENT_COUNT];
slime73@11819
   166
} METAL_ShaderPipelines;
slime73@11819
   167
slouken@11732
   168
@interface METAL_RenderData : NSObject
slouken@11735
   169
    @property (nonatomic, retain) id<MTLDevice> mtldevice;
slouken@11735
   170
    @property (nonatomic, retain) id<MTLCommandQueue> mtlcmdqueue;
slouken@11735
   171
    @property (nonatomic, retain) id<MTLCommandBuffer> mtlcmdbuffer;
slouken@11735
   172
    @property (nonatomic, retain) id<MTLRenderCommandEncoder> mtlcmdencoder;
slouken@11735
   173
    @property (nonatomic, retain) id<MTLLibrary> mtllibrary;
slouken@11735
   174
    @property (nonatomic, retain) id<CAMetalDrawable> mtlbackbuffer;
slime73@11801
   175
    @property (nonatomic, retain) id<MTLSamplerState> mtlsamplernearest;
slime73@11801
   176
    @property (nonatomic, retain) id<MTLSamplerState> mtlsamplerlinear;
slime73@11810
   177
    @property (nonatomic, retain) id<MTLBuffer> mtlbufconstants;
slouken@11735
   178
    @property (nonatomic, retain) CAMetalLayer *mtllayer;
slouken@11735
   179
    @property (nonatomic, retain) MTLRenderPassDescriptor *mtlpassdesc;
slime73@11820
   180
    @property (nonatomic, assign) METAL_ShaderPipelines *activepipelines;
slime73@11820
   181
    @property (nonatomic, assign) METAL_ShaderPipelines *allpipelines;
slime73@11820
   182
    @property (nonatomic, assign) int pipelinescount;
slouken@11732
   183
@end
icculus@11729
   184
slouken@11732
   185
@implementation METAL_RenderData
slime73@11805
   186
#if !__has_feature(objc_arc)
slime73@11804
   187
- (void)dealloc
slime73@11804
   188
{
slime73@11804
   189
    [_mtldevice release];
slime73@11804
   190
    [_mtlcmdqueue release];
slime73@11804
   191
    [_mtlcmdbuffer release];
slime73@11804
   192
    [_mtlcmdencoder release];
slime73@11804
   193
    [_mtllibrary release];
slime73@11804
   194
    [_mtlbackbuffer release];
slime73@11804
   195
    [_mtlsamplernearest release];
slime73@11804
   196
    [_mtlsamplerlinear release];
slime73@11810
   197
    [_mtlbufconstants release];
slime73@11804
   198
    [_mtllayer release];
slime73@11804
   199
    [_mtlpassdesc release];
slime73@11804
   200
    [super dealloc];
slime73@11804
   201
}
slime73@11804
   202
#endif
slouken@11732
   203
@end
icculus@11729
   204
slouken@11753
   205
@interface METAL_TextureData : NSObject
slouken@11753
   206
    @property (nonatomic, retain) id<MTLTexture> mtltexture;
slime73@11819
   207
    @property (nonatomic, retain) id<MTLTexture> mtltexture_uv;
slime73@11801
   208
    @property (nonatomic, retain) id<MTLSamplerState> mtlsampler;
slime73@11819
   209
    @property (nonatomic, assign) SDL_MetalFragmentFunction fragmentFunction;
slime73@11819
   210
    @property (nonatomic, assign) BOOL yuv;
slime73@11819
   211
    @property (nonatomic, assign) BOOL nv12;
slime73@11819
   212
    @property (nonatomic, assign) size_t conversionBufferOffset;
slouken@11753
   213
@end
slouken@11753
   214
slouken@11753
   215
@implementation METAL_TextureData
slime73@11805
   216
#if !__has_feature(objc_arc)
slime73@11804
   217
- (void)dealloc
slime73@11804
   218
{
slime73@11804
   219
    [_mtltexture release];
slime73@11819
   220
    [_mtltexture_uv release];
slime73@11804
   221
    [_mtlsampler release];
slime73@11804
   222
    [super dealloc];
slime73@11804
   223
}
slime73@11804
   224
#endif
slouken@11753
   225
@end
slouken@11753
   226
icculus@11729
   227
static int
icculus@11729
   228
IsMetalAvailable(const SDL_SysWMinfo *syswm)
icculus@11729
   229
{
slouken@11733
   230
    if (syswm->subsystem != SDL_SYSWM_COCOA && syswm->subsystem != SDL_SYSWM_UIKIT) {
slouken@11733
   231
        return SDL_SetError("Metal render target only supports Cocoa and UIKit video targets at the moment.");
icculus@11729
   232
    }
icculus@11729
   233
icculus@11729
   234
    // this checks a weak symbol.
icculus@11742
   235
#if (defined(__MACOSX__) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101100))
icculus@11729
   236
    if (MTLCreateSystemDefaultDevice == NULL) {  // probably on 10.10 or lower.
icculus@11729
   237
        return SDL_SetError("Metal framework not available on this system");
icculus@11729
   238
    }
icculus@11729
   239
#endif
icculus@11729
   240
icculus@11729
   241
    return 0;
icculus@11729
   242
}
icculus@11729
   243
slime73@11800
   244
static const MTLBlendOperation invalidBlendOperation = (MTLBlendOperation)0xFFFFFFFF;
slime73@11800
   245
static const MTLBlendFactor invalidBlendFactor = (MTLBlendFactor)0xFFFFFFFF;
slime73@11800
   246
slime73@11800
   247
static MTLBlendOperation
slime73@11800
   248
GetBlendOperation(SDL_BlendOperation operation)
slime73@11800
   249
{
slime73@11800
   250
    switch (operation) {
slime73@11800
   251
        case SDL_BLENDOPERATION_ADD: return MTLBlendOperationAdd;
slime73@11800
   252
        case SDL_BLENDOPERATION_SUBTRACT: return MTLBlendOperationSubtract;
slime73@11800
   253
        case SDL_BLENDOPERATION_REV_SUBTRACT: return MTLBlendOperationReverseSubtract;
slime73@11800
   254
        case SDL_BLENDOPERATION_MINIMUM: return MTLBlendOperationMin;
slime73@11800
   255
        case SDL_BLENDOPERATION_MAXIMUM: return MTLBlendOperationMax;
slime73@11800
   256
        default: return invalidBlendOperation;
slime73@11800
   257
    }
slime73@11800
   258
}
slime73@11800
   259
slime73@11800
   260
static MTLBlendFactor
slime73@11800
   261
GetBlendFactor(SDL_BlendFactor factor)
slime73@11800
   262
{
slime73@11800
   263
    switch (factor) {
slime73@11800
   264
        case SDL_BLENDFACTOR_ZERO: return MTLBlendFactorZero;
slime73@11800
   265
        case SDL_BLENDFACTOR_ONE: return MTLBlendFactorOne;
slime73@11800
   266
        case SDL_BLENDFACTOR_SRC_COLOR: return MTLBlendFactorSourceColor;
slime73@11800
   267
        case SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return MTLBlendFactorOneMinusSourceColor;
slime73@11800
   268
        case SDL_BLENDFACTOR_SRC_ALPHA: return MTLBlendFactorSourceAlpha;
slime73@11800
   269
        case SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return MTLBlendFactorOneMinusSourceAlpha;
slime73@11800
   270
        case SDL_BLENDFACTOR_DST_COLOR: return MTLBlendFactorDestinationColor;
slime73@11800
   271
        case SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR: return MTLBlendFactorOneMinusDestinationColor;
slime73@11800
   272
        case SDL_BLENDFACTOR_DST_ALPHA: return MTLBlendFactorDestinationAlpha;
slime73@11800
   273
        case SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return MTLBlendFactorOneMinusDestinationAlpha;
slime73@11800
   274
        default: return invalidBlendFactor;
slime73@11800
   275
    }
slime73@11800
   276
}
slime73@11800
   277
slime73@11800
   278
static NSString *
slime73@11800
   279
GetVertexFunctionName(SDL_MetalVertexFunction function)
slime73@11800
   280
{
slime73@11800
   281
    switch (function) {
slime73@11800
   282
        case SDL_METAL_VERTEX_SOLID: return @"SDL_Solid_vertex";
slime73@11800
   283
        case SDL_METAL_VERTEX_COPY: return @"SDL_Copy_vertex";
slime73@11800
   284
        default: return nil;
slime73@11800
   285
    }
slime73@11800
   286
}
slime73@11800
   287
slime73@11800
   288
static NSString *
slime73@11800
   289
GetFragmentFunctionName(SDL_MetalFragmentFunction function)
slime73@11800
   290
{
slime73@11800
   291
    switch (function) {
slime73@11800
   292
        case SDL_METAL_FRAGMENT_SOLID: return @"SDL_Solid_fragment";
slime73@11801
   293
        case SDL_METAL_FRAGMENT_COPY: return @"SDL_Copy_fragment";
slime73@11819
   294
        case SDL_METAL_FRAGMENT_YUV: return @"SDL_YUV_fragment";
slime73@11819
   295
        case SDL_METAL_FRAGMENT_NV12: return @"SDL_NV12_fragment";
slime73@11819
   296
        case SDL_METAL_FRAGMENT_NV21: return @"SDL_NV21_fragment";
slime73@11800
   297
        default: return nil;
slime73@11800
   298
    }
slime73@11800
   299
}
slime73@11800
   300
icculus@11729
   301
static id<MTLRenderPipelineState>
slime73@11800
   302
MakePipelineState(METAL_RenderData *data, METAL_PipelineCache *cache,
slime73@11800
   303
                  NSString *blendlabel, SDL_BlendMode blendmode)
icculus@11729
   304
{
slime73@11800
   305
    id<MTLFunction> mtlvertfn = [data.mtllibrary newFunctionWithName:GetVertexFunctionName(cache->vertexFunction)];
slime73@11800
   306
    id<MTLFunction> mtlfragfn = [data.mtllibrary newFunctionWithName:GetFragmentFunctionName(cache->fragmentFunction)];
icculus@11729
   307
    SDL_assert(mtlvertfn != nil);
icculus@11729
   308
    SDL_assert(mtlfragfn != nil);
icculus@11729
   309
icculus@11729
   310
    MTLRenderPipelineDescriptor *mtlpipedesc = [[MTLRenderPipelineDescriptor alloc] init];
icculus@11729
   311
    mtlpipedesc.vertexFunction = mtlvertfn;
icculus@11729
   312
    mtlpipedesc.fragmentFunction = mtlfragfn;
icculus@11729
   313
slime73@11800
   314
    MTLRenderPipelineColorAttachmentDescriptor *rtdesc = mtlpipedesc.colorAttachments[0];
icculus@11729
   315
slime73@11820
   316
    rtdesc.pixelFormat = cache->renderTargetFormat;
icculus@11729
   317
slime73@11800
   318
    if (blendmode != SDL_BLENDMODE_NONE) {
slime73@11800
   319
        rtdesc.blendingEnabled = YES;
slime73@11800
   320
        rtdesc.sourceRGBBlendFactor = GetBlendFactor(SDL_GetBlendModeSrcColorFactor(blendmode));
slime73@11800
   321
        rtdesc.destinationRGBBlendFactor = GetBlendFactor(SDL_GetBlendModeDstColorFactor(blendmode));
slime73@11800
   322
        rtdesc.rgbBlendOperation = GetBlendOperation(SDL_GetBlendModeColorOperation(blendmode));
slime73@11800
   323
        rtdesc.sourceAlphaBlendFactor = GetBlendFactor(SDL_GetBlendModeSrcAlphaFactor(blendmode));
slime73@11800
   324
        rtdesc.destinationAlphaBlendFactor = GetBlendFactor(SDL_GetBlendModeDstAlphaFactor(blendmode));
slime73@11800
   325
        rtdesc.alphaBlendOperation = GetBlendOperation(SDL_GetBlendModeAlphaOperation(blendmode));
slime73@11800
   326
    } else {
slime73@11800
   327
        rtdesc.blendingEnabled = NO;
icculus@11729
   328
    }
icculus@11729
   329
slime73@11800
   330
    mtlpipedesc.label = [@(cache->label) stringByAppendingString:blendlabel];
icculus@11729
   331
icculus@11729
   332
    NSError *err = nil;
slime73@11800
   333
    id<MTLRenderPipelineState> state = [data.mtldevice newRenderPipelineStateWithDescriptor:mtlpipedesc error:&err];
icculus@11729
   334
    SDL_assert(err == nil);
slime73@11800
   335
slime73@11800
   336
    METAL_PipelineState pipeline;
slime73@11800
   337
    pipeline.blendMode = blendmode;
slime73@11800
   338
    pipeline.pipe = (void *)CFBridgingRetain(state);
slime73@11800
   339
slime73@11800
   340
    METAL_PipelineState *states = SDL_realloc(cache->states, (cache->count + 1) * sizeof(pipeline));
slime73@11800
   341
slouken@11732
   342
#if !__has_feature(objc_arc)
icculus@11729
   343
    [mtlpipedesc release];  // !!! FIXME: can these be reused for each creation, or does the pipeline obtain it?
icculus@11729
   344
    [mtlvertfn release];
icculus@11729
   345
    [mtlfragfn release];
slime73@11800
   346
    [state release];
slouken@11732
   347
#endif
slime73@11800
   348
slime73@11800
   349
    if (states) {
slime73@11800
   350
        states[cache->count++] = pipeline;
slime73@11800
   351
        cache->states = states;
slime73@11800
   352
        return (__bridge id<MTLRenderPipelineState>)pipeline.pipe;
slime73@11800
   353
    } else {
slime73@11800
   354
        CFBridgingRelease(pipeline.pipe);
slime73@11800
   355
        SDL_OutOfMemory();
slime73@11800
   356
        return NULL;
slime73@11800
   357
    }
slime73@11800
   358
}
slime73@11800
   359
slime73@11819
   360
static void
slime73@11820
   361
MakePipelineCache(METAL_RenderData *data, METAL_PipelineCache *cache, const char *label,
slime73@11820
   362
                  MTLPixelFormat rtformat, SDL_MetalVertexFunction vertfn, SDL_MetalFragmentFunction fragfn)
slime73@11800
   363
{
slime73@11800
   364
    SDL_zerop(cache);
slime73@11800
   365
slime73@11800
   366
    cache->vertexFunction = vertfn;
slime73@11800
   367
    cache->fragmentFunction = fragfn;
slime73@11820
   368
    cache->renderTargetFormat = rtformat;
slime73@11800
   369
    cache->label = label;
slime73@11800
   370
slime73@11800
   371
    /* Create pipeline states for the default blend modes. Custom blend modes
slime73@11800
   372
     * will be added to the cache on-demand. */
slime73@11819
   373
    MakePipelineState(data, cache, @" (blend=none)", SDL_BLENDMODE_NONE);
slime73@11819
   374
    MakePipelineState(data, cache, @" (blend=blend)", SDL_BLENDMODE_BLEND);
slime73@11819
   375
    MakePipelineState(data, cache, @" (blend=add)", SDL_BLENDMODE_ADD);
slime73@11819
   376
    MakePipelineState(data, cache, @" (blend=mod)", SDL_BLENDMODE_MOD);
icculus@11729
   377
}
icculus@11729
   378
icculus@11729
   379
static void
slime73@11800
   380
DestroyPipelineCache(METAL_PipelineCache *cache)
icculus@11729
   381
{
slime73@11800
   382
    if (cache != NULL) {
slime73@11800
   383
        for (int i = 0; i < cache->count; i++) {
slime73@11800
   384
            CFBridgingRelease(cache->states[i].pipe);
slime73@11800
   385
        }
slime73@11800
   386
slime73@11800
   387
        SDL_free(cache->states);
slime73@11819
   388
    }
slime73@11819
   389
}
slime73@11819
   390
slime73@11820
   391
void
slime73@11820
   392
MakeShaderPipelines(METAL_RenderData *data, METAL_ShaderPipelines *pipelines, MTLPixelFormat rtformat)
slime73@11820
   393
{
slime73@11820
   394
    SDL_zerop(pipelines);
slime73@11820
   395
slime73@11820
   396
    pipelines->renderTargetFormat = rtformat;
slime73@11820
   397
slime73@11820
   398
    MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_SOLID], "SDL primitives pipeline", rtformat, SDL_METAL_VERTEX_SOLID, SDL_METAL_FRAGMENT_SOLID);
slime73@11820
   399
    MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_COPY], "SDL copy pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_COPY);
slime73@11820
   400
    MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_YUV], "SDL YUV pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_YUV);
slime73@11820
   401
    MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_NV12], "SDL NV12 pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_NV12);
slime73@11820
   402
    MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_NV21], "SDL NV21 pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_NV21);
slime73@11820
   403
}
slime73@11820
   404
slime73@11819
   405
static METAL_ShaderPipelines *
slime73@11820
   406
ChooseShaderPipelines(METAL_RenderData *data, MTLPixelFormat rtformat)
slime73@11819
   407
{
slime73@11820
   408
    METAL_ShaderPipelines *allpipelines = data.allpipelines;
slime73@11820
   409
    int count = data.pipelinescount;
slime73@11820
   410
slime73@11820
   411
    for (int i = 0; i < count; i++) {
slime73@11820
   412
        if (allpipelines[i].renderTargetFormat == rtformat) {
slime73@11820
   413
            return &allpipelines[i];
slime73@11820
   414
        }
slime73@11820
   415
    }
slime73@11820
   416
slime73@11820
   417
    allpipelines = SDL_realloc(allpipelines, (count + 1) * sizeof(METAL_ShaderPipelines));
slime73@11820
   418
slime73@11820
   419
    if (allpipelines == NULL) {
slime73@11819
   420
        SDL_OutOfMemory();
slime73@11819
   421
        return NULL;
slime73@11819
   422
    }
slime73@11819
   423
slime73@11820
   424
    MakeShaderPipelines(data, &allpipelines[count], rtformat);
slime73@11819
   425
slime73@11820
   426
    data.allpipelines = allpipelines;
slime73@11820
   427
    data.pipelinescount = count + 1;
slime73@11820
   428
slime73@11820
   429
    return &data.allpipelines[count];
slime73@11819
   430
}
slime73@11819
   431
slime73@11819
   432
static void
slime73@11820
   433
DestroyAllPipelines(METAL_ShaderPipelines *allpipelines, int count)
slime73@11819
   434
{
slime73@11820
   435
    if (allpipelines != NULL) {
slime73@11820
   436
        for (int i = 0; i < count; i++) {
slime73@11820
   437
            for (int cache = 0; cache < SDL_METAL_FRAGMENT_COUNT; cache++) {
slime73@11820
   438
                DestroyPipelineCache(&allpipelines[i].caches[cache]);
slime73@11820
   439
            }
slime73@11819
   440
        }
slime73@11819
   441
slime73@11820
   442
        SDL_free(allpipelines);
slime73@11800
   443
    }
icculus@11729
   444
}
icculus@11729
   445
icculus@11729
   446
static inline id<MTLRenderPipelineState>
slime73@11819
   447
ChoosePipelineState(METAL_RenderData *data, METAL_ShaderPipelines *pipelines, SDL_MetalFragmentFunction fragfn, SDL_BlendMode blendmode)
icculus@11729
   448
{
slime73@11819
   449
    METAL_PipelineCache *cache = &pipelines->caches[fragfn];
slime73@11819
   450
slime73@11800
   451
    for (int i = 0; i < cache->count; i++) {
slime73@11800
   452
        if (cache->states[i].blendMode == blendmode) {
slime73@11800
   453
            return (__bridge id<MTLRenderPipelineState>)cache->states[i].pipe;
slime73@11800
   454
        }
icculus@11729
   455
    }
slime73@11800
   456
slime73@11819
   457
    return MakePipelineState(data, cache, [NSString stringWithFormat:@" (blend=custom 0x%x)", blendmode], blendmode);
icculus@11729
   458
}
icculus@11729
   459
icculus@11729
   460
static SDL_Renderer *
icculus@11729
   461
METAL_CreateRenderer(SDL_Window * window, Uint32 flags)
slime73@11821
   462
{ @autoreleasepool {
icculus@11729
   463
    SDL_Renderer *renderer = NULL;
icculus@11729
   464
    METAL_RenderData *data = NULL;
slime73@11821
   465
    id<MTLDevice> mtldevice = nil;
icculus@11729
   466
    SDL_SysWMinfo syswm;
icculus@11729
   467
icculus@11729
   468
    SDL_VERSION(&syswm.version);
icculus@11729
   469
    if (!SDL_GetWindowWMInfo(window, &syswm)) {
icculus@11729
   470
        return NULL;
icculus@11729
   471
    }
icculus@11729
   472
icculus@11729
   473
    if (IsMetalAvailable(&syswm) == -1) {
icculus@11729
   474
        return NULL;
icculus@11729
   475
    }
icculus@11729
   476
slouken@11732
   477
    renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
slouken@11732
   478
    if (!renderer) {
icculus@11729
   479
        SDL_OutOfMemory();
icculus@11729
   480
        return NULL;
icculus@11729
   481
    }
icculus@11729
   482
slime73@11821
   483
    // !!! FIXME: MTLCopyAllDevices() can find other GPUs on macOS...
slime73@11821
   484
    mtldevice = MTLCreateSystemDefaultDevice();
slime73@11821
   485
slime73@11821
   486
    if (mtldevice == nil) {
slime73@11821
   487
        SDL_free(renderer);
slime73@11821
   488
        SDL_SetError("Failed to obtain Metal device");
slime73@11821
   489
        return NULL;
slime73@11821
   490
    }
slime73@11821
   491
slime73@11821
   492
    // !!! FIXME: error checking on all of this.
slouken@11732
   493
    data = [[METAL_RenderData alloc] init];
icculus@11729
   494
slouken@11732
   495
    renderer->driverdata = (void*)CFBridgingRetain(data);
icculus@11729
   496
    renderer->window = window;
icculus@11729
   497
slouken@11730
   498
#ifdef __MACOSX__
slime73@11798
   499
    NSView *view = Cocoa_Mtl_AddMetalView(window);
slime73@11798
   500
    CAMetalLayer *layer = (CAMetalLayer *)[view layer];
icculus@11729
   501
slouken@11730
   502
    layer.device = mtldevice;
slime73@11798
   503
icculus@11729
   504
    //layer.colorspace = nil;
icculus@11729
   505
slouken@11730
   506
#else
slouken@11732
   507
    UIView *view = UIKit_Mtl_AddMetalView(window);
slouken@11732
   508
    CAMetalLayer *layer = (CAMetalLayer *)[view layer];
slouken@11730
   509
#endif
slouken@11730
   510
slime73@11800
   511
    // Necessary for RenderReadPixels.
slime73@11800
   512
    layer.framebufferOnly = NO;
slime73@11800
   513
slouken@11732
   514
    data.mtldevice = layer.device;
slouken@11732
   515
    data.mtllayer = layer;
slime73@11804
   516
    id<MTLCommandQueue> mtlcmdqueue = [data.mtldevice newCommandQueue];
slime73@11804
   517
    data.mtlcmdqueue = mtlcmdqueue;
slouken@11732
   518
    data.mtlcmdqueue.label = @"SDL Metal Renderer";
icculus@11749
   519
    data.mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor];
icculus@11729
   520
slouken@11735
   521
    NSError *err = nil;
icculus@11729
   522
slouken@11735
   523
    // The compiled .metallib is embedded in a static array in a header file
slouken@11735
   524
    // but the original shader source code is in SDL_shaders_metal.metal.
slouken@11735
   525
    dispatch_data_t mtllibdata = dispatch_data_create(sdl_metallib, sdl_metallib_len, dispatch_get_global_queue(0, 0), ^{});
slime73@11804
   526
    id<MTLLibrary> mtllibrary = [data.mtldevice newLibraryWithData:mtllibdata error:&err];
slime73@11804
   527
    data.mtllibrary = mtllibrary;
slouken@11735
   528
    SDL_assert(err == nil);
slouken@11735
   529
#if !__has_feature(objc_arc)
slouken@11735
   530
    dispatch_release(mtllibdata);
slouken@11735
   531
#endif
slouken@11735
   532
    data.mtllibrary.label = @"SDL Metal renderer shader library";
icculus@11729
   533
slime73@11820
   534
    /* Do some shader pipeline state loading up-front rather than on demand. */
slime73@11820
   535
    data.pipelinescount = 0;
slime73@11820
   536
    data.allpipelines = NULL;
slime73@11820
   537
    ChooseShaderPipelines(data, MTLPixelFormatBGRA8Unorm);
slime73@11801
   538
slime73@11802
   539
    MTLSamplerDescriptor *samplerdesc = [[MTLSamplerDescriptor alloc] init];
slime73@11801
   540
slime73@11801
   541
    samplerdesc.minFilter = MTLSamplerMinMagFilterNearest;
slime73@11801
   542
    samplerdesc.magFilter = MTLSamplerMinMagFilterNearest;
slime73@11804
   543
    id<MTLSamplerState> mtlsamplernearest = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc];
slime73@11804
   544
    data.mtlsamplernearest = mtlsamplernearest;
slime73@11801
   545
slime73@11801
   546
    samplerdesc.minFilter = MTLSamplerMinMagFilterLinear;
slime73@11801
   547
    samplerdesc.magFilter = MTLSamplerMinMagFilterLinear;
slime73@11804
   548
    id<MTLSamplerState> mtlsamplerlinear = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc];
slime73@11804
   549
    data.mtlsamplerlinear = mtlsamplerlinear;
slime73@11802
   550
slime73@11810
   551
    /* Note: matrices are column major. */
slime73@11810
   552
    float identitytransform[16] = {
slime73@11810
   553
        1.0f, 0.0f, 0.0f, 0.0f,
slime73@11810
   554
        0.0f, 1.0f, 0.0f, 0.0f,
slime73@11810
   555
        0.0f, 0.0f, 1.0f, 0.0f,
slime73@11810
   556
        0.0f, 0.0f, 0.0f, 1.0f,
slime73@11810
   557
    };
slouken@11735
   558
slime73@11810
   559
    float halfpixeltransform[16] = {
slime73@11810
   560
        1.0f, 0.0f, 0.0f, 0.0f,
slime73@11810
   561
        0.0f, 1.0f, 0.0f, 0.0f,
slime73@11810
   562
        0.0f, 0.0f, 1.0f, 0.0f,
slime73@11810
   563
        0.5f, 0.5f, 0.0f, 1.0f,
slime73@11810
   564
    };
slime73@11810
   565
slime73@11819
   566
    /* Metal pads float3s to 16 bytes. */
slime73@11819
   567
    float decodetransformJPEG[4*4] = {
slime73@11819
   568
        0.0, -0.501960814, -0.501960814, 0.0, /* offset */
slime73@11819
   569
        1.0000,  0.0000,  1.4020, 0.0,        /* Rcoeff */
slime73@11819
   570
        1.0000, -0.3441, -0.7141, 0.0,        /* Gcoeff */
slime73@11819
   571
        1.0000,  1.7720,  0.0000, 0.0,        /* Bcoeff */
slime73@11819
   572
    };
slime73@11819
   573
slime73@11819
   574
    float decodetransformBT601[4*4] = {
slime73@11819
   575
        -0.0627451017, -0.501960814, -0.501960814, 0.0, /* offset */
slime73@11819
   576
        1.1644,  0.0000,  1.5960, 0.0,                  /* Rcoeff */
slime73@11819
   577
        1.1644, -0.3918, -0.8130, 0.0,                  /* Gcoeff */
slime73@11819
   578
        1.1644,  2.0172,  0.0000, 0.0,                  /* Bcoeff */
slime73@11819
   579
    };
slime73@11819
   580
slime73@11819
   581
    float decodetransformBT709[4*4] = {
slime73@11819
   582
        0.0, -0.501960814, -0.501960814, 0.0, /* offset */
slime73@11819
   583
        1.0000,  0.0000,  1.4020, 0.0,        /* Rcoeff */
slime73@11819
   584
        1.0000, -0.3441, -0.7141, 0.0,        /* Gcoeff */
slime73@11819
   585
        1.0000,  1.7720,  0.0000, 0.0,        /* Bcoeff */
slime73@11819
   586
    };
slime73@11819
   587
slime73@11810
   588
    float clearverts[6] = {0.0f, 0.0f,  0.0f, 2.0f,  2.0f, 0.0f};
slime73@11810
   589
slime73@11818
   590
    id<MTLBuffer> mtlbufconstantstaging = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModeShared];
slime73@11818
   591
    mtlbufconstantstaging.label = @"SDL constant staging data";
slime73@11810
   592
slime73@11818
   593
    id<MTLBuffer> mtlbufconstants = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModePrivate];
slime73@11810
   594
    data.mtlbufconstants = mtlbufconstants;
slime73@11810
   595
    data.mtlbufconstants.label = @"SDL constant data";
slime73@11810
   596
slime73@11818
   597
    char *constantdata = [mtlbufconstantstaging contents];
slime73@11810
   598
    SDL_memcpy(constantdata + CONSTANTS_OFFSET_IDENTITY, identitytransform, sizeof(identitytransform));
slime73@11810
   599
    SDL_memcpy(constantdata + CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM, halfpixeltransform, sizeof(halfpixeltransform));
slime73@11819
   600
    SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_JPEG, decodetransformJPEG, sizeof(decodetransformJPEG));
slime73@11819
   601
    SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT601, decodetransformBT601, sizeof(decodetransformBT601));
slime73@11819
   602
    SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT709, decodetransformBT709, sizeof(decodetransformBT709));
slime73@11810
   603
    SDL_memcpy(constantdata + CONSTANTS_OFFSET_CLEAR_VERTS, clearverts, sizeof(clearverts));
slime73@11818
   604
slime73@11818
   605
    id<MTLCommandBuffer> cmdbuffer = [data.mtlcmdqueue commandBuffer];
slime73@11818
   606
    id<MTLBlitCommandEncoder> blitcmd = [cmdbuffer blitCommandEncoder];
slime73@11818
   607
slime73@11818
   608
    [blitcmd copyFromBuffer:mtlbufconstantstaging sourceOffset:0 toBuffer:data.mtlbufconstants destinationOffset:0 size:CONSTANTS_LENGTH];
slime73@11818
   609
slime73@11818
   610
    [blitcmd endEncoding];
slime73@11818
   611
    [cmdbuffer commit];
slime73@11799
   612
slouken@11735
   613
    // !!! FIXME: force more clears here so all the drawables are sane to start, and our static buffers are definitely flushed.
icculus@11729
   614
icculus@11729
   615
    renderer->WindowEvent = METAL_WindowEvent;
icculus@11729
   616
    renderer->GetOutputSize = METAL_GetOutputSize;
slime73@11800
   617
    renderer->SupportsBlendMode = METAL_SupportsBlendMode;
icculus@11729
   618
    renderer->CreateTexture = METAL_CreateTexture;
icculus@11729
   619
    renderer->UpdateTexture = METAL_UpdateTexture;
icculus@11729
   620
    renderer->UpdateTextureYUV = METAL_UpdateTextureYUV;
icculus@11729
   621
    renderer->LockTexture = METAL_LockTexture;
icculus@11729
   622
    renderer->UnlockTexture = METAL_UnlockTexture;
icculus@11729
   623
    renderer->SetRenderTarget = METAL_SetRenderTarget;
icculus@11729
   624
    renderer->UpdateViewport = METAL_UpdateViewport;
icculus@11729
   625
    renderer->UpdateClipRect = METAL_UpdateClipRect;
icculus@11729
   626
    renderer->RenderClear = METAL_RenderClear;
icculus@11729
   627
    renderer->RenderDrawPoints = METAL_RenderDrawPoints;
icculus@11729
   628
    renderer->RenderDrawLines = METAL_RenderDrawLines;
icculus@11729
   629
    renderer->RenderFillRects = METAL_RenderFillRects;
icculus@11729
   630
    renderer->RenderCopy = METAL_RenderCopy;
icculus@11729
   631
    renderer->RenderCopyEx = METAL_RenderCopyEx;
icculus@11729
   632
    renderer->RenderReadPixels = METAL_RenderReadPixels;
icculus@11729
   633
    renderer->RenderPresent = METAL_RenderPresent;
icculus@11729
   634
    renderer->DestroyTexture = METAL_DestroyTexture;
icculus@11729
   635
    renderer->DestroyRenderer = METAL_DestroyRenderer;
slouken@11744
   636
    renderer->GetMetalLayer = METAL_GetMetalLayer;
slouken@11744
   637
    renderer->GetMetalCommandEncoder = METAL_GetMetalCommandEncoder;
icculus@11729
   638
icculus@11729
   639
    renderer->info = METAL_RenderDriver.info;
icculus@11729
   640
    renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
icculus@11729
   641
slime73@11790
   642
#if defined(__MACOSX__) && defined(MAC_OS_X_VERSION_10_13)
slime73@11790
   643
    if (@available(macOS 10.13, *)) {
slime73@11798
   644
        data.mtllayer.displaySyncEnabled = (flags & SDL_RENDERER_PRESENTVSYNC) != 0;
slime73@11790
   645
    } else
slime73@11790
   646
#endif
slime73@11790
   647
    {
slime73@11790
   648
        renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
slime73@11790
   649
    }
icculus@11729
   650
slime73@11821
   651
    /* https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */
slime73@11821
   652
    int maxtexsize = 4096;
slime73@11821
   653
#if defined(__MACOSX__)
slime73@11821
   654
    maxtexsize = 16384;
slime73@11821
   655
#elif defined(__TVOS__)
slouken@11822
   656
    maxtexsize = 8192;
slime73@11821
   657
#ifdef __TVOS_11_0
slouken@11822
   658
    if (@available(tvOS 11.0, *)) {
slouken@11822
   659
        if ([mtldevice supportsFeatureSet:MTLFeatureSet_tvOS_GPUFamily2_v1]) {
slouken@11822
   660
            maxtexsize = 16384;
slouken@11822
   661
        }
slouken@11822
   662
    }
slime73@11821
   663
#endif
slime73@11821
   664
#else
slime73@11821
   665
#ifdef __IPHONE_11_0
slime73@11821
   666
    if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily4_v1]) {
slime73@11821
   667
        maxtexsize = 16384;
slime73@11821
   668
    } else
slime73@11821
   669
#endif
slime73@11821
   670
#ifdef __IPHONE_10_0
slime73@11821
   671
    if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
slime73@11821
   672
        maxtexsize = 16384;
slime73@11821
   673
    } else
slime73@11821
   674
#endif
slime73@11821
   675
    if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v2] || [mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v2]) {
slime73@11821
   676
        maxtexsize = 8192;
slime73@11821
   677
    } else {
slime73@11821
   678
        maxtexsize = 4096;
slime73@11821
   679
    }
slime73@11821
   680
#endif
slime73@11821
   681
slime73@11821
   682
    renderer->info.max_texture_width = maxtexsize;
slime73@11821
   683
    renderer->info.max_texture_height = maxtexsize;
slime73@11821
   684
slime73@11804
   685
#if !__has_feature(objc_arc)
slime73@11804
   686
    [mtlcmdqueue release];
slime73@11804
   687
    [mtllibrary release];
slime73@11804
   688
    [samplerdesc release];
slime73@11804
   689
    [mtlsamplernearest release];
slime73@11804
   690
    [mtlsamplerlinear release];
slime73@11810
   691
    [mtlbufconstants release];
slime73@11804
   692
    [view release];
slime73@11804
   693
    [data release];
slime73@11804
   694
    [mtldevice release];
slime73@11804
   695
#endif
slime73@11804
   696
slouken@11735
   697
    return renderer;
slime73@11821
   698
}}
icculus@11729
   699
slime73@11804
   700
static void
slime73@11817
   701
METAL_ActivateRenderCommandEncoder(SDL_Renderer * renderer, MTLLoadAction load)
slouken@11735
   702
{
slouken@11735
   703
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@11729
   704
slime73@11817
   705
    /* Our SetRenderTarget just signals that the next render operation should
slime73@11817
   706
     * set up a new render pass. This is where that work happens. */
slime73@11817
   707
    if (data.mtlcmdencoder == nil) {
slime73@11817
   708
        id<MTLTexture> mtltexture = nil;
slime73@11817
   709
slime73@11817
   710
        if (renderer->target != NULL) {
slime73@11817
   711
            METAL_TextureData *texdata = (__bridge METAL_TextureData *)renderer->target->driverdata;
slime73@11817
   712
            mtltexture = texdata.mtltexture;
slime73@11817
   713
        } else {
slime73@11817
   714
            if (data.mtlbackbuffer == nil) {
slime73@11817
   715
                /* The backbuffer's contents aren't guaranteed to persist after
slime73@11817
   716
                 * presenting, so we can leave it undefined when loading it. */
slime73@11817
   717
                data.mtlbackbuffer = [data.mtllayer nextDrawable];
slime73@11817
   718
                if (load == MTLLoadActionLoad) {
slime73@11817
   719
                    load = MTLLoadActionDontCare;
slime73@11817
   720
                }
slime73@11817
   721
            }
slime73@11817
   722
            mtltexture = data.mtlbackbuffer.texture;
slime73@11817
   723
        }
slime73@11817
   724
slime73@11817
   725
        SDL_assert(mtltexture);
slime73@11817
   726
slime73@11817
   727
        if (load == MTLLoadActionClear) {
slime73@11817
   728
            MTLClearColor color = MTLClearColorMake(renderer->r/255.0, renderer->g/255.0, renderer->b/255.0, renderer->a/255.0);
slime73@11817
   729
            data.mtlpassdesc.colorAttachments[0].clearColor = color;
slime73@11817
   730
        }
slime73@11817
   731
slime73@11817
   732
        data.mtlpassdesc.colorAttachments[0].loadAction = load;
slime73@11817
   733
        data.mtlpassdesc.colorAttachments[0].texture = mtltexture;
slime73@11817
   734
slouken@11735
   735
        data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
slouken@11735
   736
        data.mtlcmdencoder = [data.mtlcmdbuffer renderCommandEncoderWithDescriptor:data.mtlpassdesc];
icculus@11729
   737
slime73@11817
   738
        if (data.mtlbackbuffer != nil && mtltexture == data.mtlbackbuffer.texture) {
slime73@11817
   739
            data.mtlcmdencoder.label = @"SDL metal renderer backbuffer";
slime73@11817
   740
        } else {
slime73@11817
   741
            data.mtlcmdencoder.label = @"SDL metal renderer render target";
slime73@11817
   742
        }
slime73@11817
   743
slime73@11820
   744
        data.activepipelines = ChooseShaderPipelines(data, mtltexture.pixelFormat);
slime73@11820
   745
slime73@11817
   746
        /* Make sure the viewport and clip rect are set on the new render pass. */
slouken@11735
   747
        METAL_UpdateViewport(renderer);
slouken@11735
   748
        METAL_UpdateClipRect(renderer);
slouken@11735
   749
    }
icculus@11729
   750
}
icculus@11729
   751
icculus@11729
   752
static void
icculus@11729
   753
METAL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
icculus@11729
   754
{
icculus@12179
   755
    if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) {
icculus@12179
   756
        METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@12179
   757
        data.mtllayer.drawableSize = CGSizeMake(event->data1, event->data2);
icculus@12179
   758
    }
icculus@12179
   759
icculus@12179
   760
    if (event->event == SDL_WINDOWEVENT_SHOWN ||
icculus@11729
   761
        event->event == SDL_WINDOWEVENT_HIDDEN) {
icculus@11729
   762
        // !!! FIXME: write me
icculus@11729
   763
    }
icculus@11729
   764
}
icculus@11729
   765
icculus@11729
   766
static int
icculus@11729
   767
METAL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
slouken@11743
   768
{ @autoreleasepool {
slime73@11798
   769
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slime73@11798
   770
    if (w) {
slime73@11798
   771
        *w = (int)data.mtllayer.drawableSize.width;
slime73@11798
   772
    }
slime73@11798
   773
    if (h) {
slime73@11798
   774
        *h = (int)data.mtllayer.drawableSize.height;
slime73@11798
   775
    }
icculus@11729
   776
    return 0;
slouken@11743
   777
}}
icculus@11729
   778
slime73@11800
   779
static SDL_bool
slime73@11800
   780
METAL_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
slime73@11800
   781
{
slime73@11800
   782
    SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode);
slime73@11800
   783
    SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode);
slime73@11800
   784
    SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode);
slime73@11800
   785
    SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode);
slime73@11800
   786
    SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode);
slime73@11800
   787
    SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode);
slime73@11800
   788
slime73@11800
   789
    if (GetBlendFactor(srcColorFactor) == invalidBlendFactor ||
slime73@11800
   790
        GetBlendFactor(srcAlphaFactor) == invalidBlendFactor ||
slime73@11800
   791
        GetBlendOperation(colorOperation) == invalidBlendOperation ||
slime73@11800
   792
        GetBlendFactor(dstColorFactor) == invalidBlendFactor ||
slime73@11800
   793
        GetBlendFactor(dstAlphaFactor) == invalidBlendFactor ||
slime73@11800
   794
        GetBlendOperation(alphaOperation) == invalidBlendOperation) {
slime73@11800
   795
        return SDL_FALSE;
slime73@11800
   796
    }
slime73@11800
   797
    return SDL_TRUE;
slime73@11800
   798
}
slime73@11800
   799
icculus@11729
   800
static int
icculus@11729
   801
METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
slouken@11743
   802
{ @autoreleasepool {
slouken@11732
   803
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slime73@11819
   804
    MTLPixelFormat pixfmt;
icculus@11729
   805
icculus@11729
   806
    switch (texture->format) {
slime73@11819
   807
        case SDL_PIXELFORMAT_ABGR8888:
slime73@11819
   808
            pixfmt = MTLPixelFormatRGBA8Unorm;
slime73@11819
   809
            break;
slime73@11819
   810
        case SDL_PIXELFORMAT_ARGB8888:
slime73@11819
   811
            pixfmt = MTLPixelFormatBGRA8Unorm;
slime73@11819
   812
            break;
slime73@11819
   813
        case SDL_PIXELFORMAT_IYUV:
slime73@11819
   814
        case SDL_PIXELFORMAT_YV12:
slime73@11819
   815
        case SDL_PIXELFORMAT_NV12:
slime73@11819
   816
        case SDL_PIXELFORMAT_NV21:
slime73@11819
   817
            pixfmt = MTLPixelFormatR8Unorm;
slime73@11819
   818
            break;
slime73@11819
   819
        default:
slime73@11819
   820
            return SDL_SetError("Texture format %s not supported by Metal", SDL_GetPixelFormatName(texture->format));
icculus@11729
   821
    }
icculus@11729
   822
slime73@11819
   823
    MTLTextureDescriptor *mtltexdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:pixfmt
icculus@11729
   824
                                            width:(NSUInteger)texture->w height:(NSUInteger)texture->h mipmapped:NO];
slime73@11810
   825
slime73@11810
   826
    /* Not available in iOS 8. */
slime73@11810
   827
    if ([mtltexdesc respondsToSelector:@selector(usage)]) {
slime73@11810
   828
        if (texture->access == SDL_TEXTUREACCESS_TARGET) {
slime73@11810
   829
            mtltexdesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
slime73@11810
   830
        } else {
slime73@11810
   831
            mtltexdesc.usage = MTLTextureUsageShaderRead;
slime73@11810
   832
        }
icculus@11750
   833
    }
icculus@11750
   834
    
slouken@11732
   835
    id<MTLTexture> mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
icculus@11729
   836
    if (mtltexture == nil) {
icculus@11729
   837
        return SDL_SetError("Texture allocation failed");
icculus@11729
   838
    }
icculus@11729
   839
slime73@11819
   840
    id<MTLTexture> mtltexture_uv = nil;
slime73@11819
   841
slime73@11819
   842
    BOOL yuv = (texture->format == SDL_PIXELFORMAT_IYUV) || (texture->format == SDL_PIXELFORMAT_YV12);
slime73@11819
   843
    BOOL nv12 = (texture->format == SDL_PIXELFORMAT_NV12) || (texture->format == SDL_PIXELFORMAT_NV21);
slime73@11819
   844
slime73@11819
   845
    if (yuv) {
slime73@11819
   846
        mtltexdesc.pixelFormat = MTLPixelFormatR8Unorm;
slime73@11819
   847
        mtltexdesc.width = (texture->w + 1) / 2;
slime73@11819
   848
        mtltexdesc.height = (texture->h + 1) / 2;
slime73@11819
   849
        mtltexdesc.textureType = MTLTextureType2DArray;
slime73@11819
   850
        mtltexdesc.arrayLength = 2;
slime73@11819
   851
        mtltexture_uv = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
slime73@11819
   852
    } else if (nv12) {
slime73@11819
   853
        mtltexdesc.pixelFormat = MTLPixelFormatRG8Unorm;
slime73@11819
   854
        mtltexdesc.width = (texture->w + 1) / 2;
slime73@11819
   855
        mtltexdesc.height = (texture->h + 1) / 2;
slime73@11819
   856
        mtltexture_uv = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
slime73@11819
   857
    }
slime73@11819
   858
slouken@11753
   859
    METAL_TextureData *texturedata = [[METAL_TextureData alloc] init];
slouken@11958
   860
    if (texture->scaleMode == SDL_ScaleModeNearest) {
slime73@11801
   861
        texturedata.mtlsampler = data.mtlsamplernearest;
slouken@11753
   862
    } else {
slime73@11801
   863
        texturedata.mtlsampler = data.mtlsamplerlinear;
slouken@11753
   864
    }
slouken@11753
   865
    texturedata.mtltexture = mtltexture;
slime73@11819
   866
    texturedata.mtltexture_uv = mtltexture_uv;
slime73@11819
   867
slime73@11819
   868
    texturedata.yuv = yuv;
slime73@11819
   869
    texturedata.nv12 = nv12;
slime73@11819
   870
slime73@11819
   871
    if (yuv) {
slime73@11819
   872
        texturedata.fragmentFunction = SDL_METAL_FRAGMENT_YUV;
slime73@11819
   873
    } else if (texture->format == SDL_PIXELFORMAT_NV12) {
slime73@11819
   874
        texturedata.fragmentFunction = SDL_METAL_FRAGMENT_NV12;
slime73@11819
   875
    } else if (texture->format == SDL_PIXELFORMAT_NV21) {
slime73@11819
   876
        texturedata.fragmentFunction = SDL_METAL_FRAGMENT_NV21;
slime73@11819
   877
    } else {
slime73@11819
   878
        texturedata.fragmentFunction = SDL_METAL_FRAGMENT_COPY;
slime73@11819
   879
    }
slime73@11819
   880
slime73@11819
   881
    if (yuv || nv12) {
slime73@11819
   882
        size_t offset = 0;
slime73@11819
   883
        SDL_YUV_CONVERSION_MODE mode = SDL_GetYUVConversionModeForResolution(texture->w, texture->h);
slime73@11819
   884
        switch (mode) {
slime73@11819
   885
            case SDL_YUV_CONVERSION_JPEG: offset = CONSTANTS_OFFSET_DECODE_JPEG; break;
slime73@11819
   886
            case SDL_YUV_CONVERSION_BT601: offset = CONSTANTS_OFFSET_DECODE_BT601; break;
slime73@11819
   887
            case SDL_YUV_CONVERSION_BT709: offset = CONSTANTS_OFFSET_DECODE_BT709; break;
slime73@11819
   888
            default: offset = 0; break;
slime73@11819
   889
        }
slime73@11819
   890
        texturedata.conversionBufferOffset = offset;
slime73@11819
   891
    }
slouken@11753
   892
slouken@11753
   893
    texture->driverdata = (void*)CFBridgingRetain(texturedata);
icculus@11729
   894
slime73@11799
   895
#if !__has_feature(objc_arc)
slime73@11804
   896
    [texturedata release];
slime73@11799
   897
    [mtltexture release];
slime73@11819
   898
    [mtltexture_uv release];
slime73@11799
   899
#endif
slime73@11799
   900
icculus@11729
   901
    return 0;
slouken@11743
   902
}}
icculus@11729
   903
icculus@11729
   904
static int
icculus@11729
   905
METAL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   906
                 const SDL_Rect * rect, const void *pixels, int pitch)
slouken@11743
   907
{ @autoreleasepool {
slime73@11819
   908
    METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
slime73@11819
   909
slime73@11820
   910
    /* !!! FIXME: replaceRegion does not do any synchronization, so it might
slime73@11820
   911
     * !!! FIXME: stomp on a previous frame's data that's currently being read
slime73@11820
   912
     * !!! FIXME: by the GPU. */
slime73@11819
   913
    [texturedata.mtltexture replaceRegion:MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h)
slime73@11819
   914
                              mipmapLevel:0
slime73@11819
   915
                                withBytes:pixels
slime73@11819
   916
                              bytesPerRow:pitch];
slime73@11819
   917
slime73@11819
   918
    if (texturedata.yuv) {
slime73@11819
   919
        int Uslice = texture->format == SDL_PIXELFORMAT_YV12 ? 1 : 0;
slime73@11819
   920
        int Vslice = texture->format == SDL_PIXELFORMAT_YV12 ? 0 : 1;
slime73@11819
   921
slime73@11819
   922
        /* Skip to the correct offset into the next texture */
slime73@11819
   923
        pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
slime73@11819
   924
        [texturedata.mtltexture_uv replaceRegion:MTLRegionMake2D(rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2)
slime73@11819
   925
                                     mipmapLevel:0
slime73@11819
   926
                                           slice:Uslice
slime73@11819
   927
                                       withBytes:pixels
slime73@11819
   928
                                     bytesPerRow:(pitch + 1) / 2
slime73@11819
   929
                                   bytesPerImage:0];
slime73@11819
   930
slime73@11819
   931
        /* Skip to the correct offset into the next texture */
slime73@11819
   932
        pixels = (const void*)((const Uint8*)pixels + ((rect->h + 1) / 2) * ((pitch + 1)/2));
slime73@11819
   933
        [texturedata.mtltexture_uv replaceRegion:MTLRegionMake2D(rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2)
slime73@11819
   934
                                     mipmapLevel:0
slime73@11819
   935
                                           slice:Vslice
slime73@11819
   936
                                       withBytes:pixels
slime73@11819
   937
                                     bytesPerRow:(pitch + 1) / 2
slime73@11819
   938
                                   bytesPerImage:0];
slime73@11819
   939
    }
slime73@11819
   940
slime73@11819
   941
    if (texturedata.nv12) {
slime73@11819
   942
        /* Skip to the correct offset into the next texture */
slime73@11819
   943
        pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
slime73@11819
   944
        [texturedata.mtltexture_uv replaceRegion:MTLRegionMake2D(rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2)
slime73@11819
   945
                                     mipmapLevel:0
slime73@11819
   946
                                           slice:0
slime73@11819
   947
                                       withBytes:pixels
slime73@11819
   948
                                     bytesPerRow:2 * ((pitch + 1) / 2)
slime73@11819
   949
                                   bytesPerImage:0];
slime73@11819
   950
    }
slime73@11819
   951
icculus@11729
   952
    return 0;
slouken@11743
   953
}}
icculus@11729
   954
icculus@11729
   955
static int
icculus@11729
   956
METAL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   957
                    const SDL_Rect * rect,
icculus@11729
   958
                    const Uint8 *Yplane, int Ypitch,
icculus@11729
   959
                    const Uint8 *Uplane, int Upitch,
icculus@11729
   960
                    const Uint8 *Vplane, int Vpitch)
slime73@11819
   961
{ @autoreleasepool {
slime73@11819
   962
    METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
icculus@12169
   963
    const int Uslice = 0;
icculus@12169
   964
    const int Vslice = 1;
slime73@11819
   965
slime73@11819
   966
    /* Bail out if we're supposed to update an empty rectangle */
slime73@11819
   967
    if (rect->w <= 0 || rect->h <= 0) {
slime73@11819
   968
        return 0;
slime73@11819
   969
    }
slime73@11819
   970
slime73@11819
   971
    [texturedata.mtltexture replaceRegion:MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h)
slime73@11819
   972
                              mipmapLevel:0
slime73@11819
   973
                                withBytes:Yplane
slime73@11819
   974
                              bytesPerRow:Ypitch];
slime73@11819
   975
slime73@11819
   976
    [texturedata.mtltexture_uv replaceRegion:MTLRegionMake2D(rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2)
slime73@11819
   977
                                 mipmapLevel:0
slime73@11819
   978
                                       slice:Uslice
slime73@11819
   979
                                   withBytes:Uplane
slime73@11819
   980
                                 bytesPerRow:Upitch
slime73@11819
   981
                               bytesPerImage:0];
slime73@11819
   982
slime73@11819
   983
    [texturedata.mtltexture_uv replaceRegion:MTLRegionMake2D(rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2)
slime73@11819
   984
                                 mipmapLevel:0
slime73@11819
   985
                                       slice:Vslice
slime73@11819
   986
                                   withBytes:Vplane
slime73@11819
   987
                                 bytesPerRow:Vpitch
slime73@11819
   988
                               bytesPerImage:0];
slime73@11819
   989
slime73@11819
   990
    return 0;
slime73@11819
   991
}}
icculus@11729
   992
icculus@11729
   993
static int
icculus@11729
   994
METAL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
   995
               const SDL_Rect * rect, void **pixels, int *pitch)
icculus@11729
   996
{
icculus@11729
   997
    return SDL_Unsupported();   // !!! FIXME: write me
icculus@11729
   998
}
icculus@11729
   999
icculus@11729
  1000
static void
icculus@11729
  1001
METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
icculus@11729
  1002
{
icculus@11729
  1003
    // !!! FIXME: write me
icculus@11729
  1004
}
icculus@11729
  1005
icculus@11729
  1006
static int
icculus@11729
  1007
METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
slouken@11743
  1008
{ @autoreleasepool {
slouken@11732
  1009
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@11750
  1010
slime73@11817
  1011
    if (data.mtlcmdencoder) {
slime73@11817
  1012
        /* End encoding for the previous render target so we can set up a new
slime73@11817
  1013
         * render pass for this one. */
slime73@11817
  1014
        [data.mtlcmdencoder endEncoding];
slime73@11817
  1015
        [data.mtlcmdbuffer commit];
icculus@11750
  1016
slime73@11817
  1017
        data.mtlcmdencoder = nil;
slime73@11817
  1018
        data.mtlcmdbuffer = nil;
slime73@11817
  1019
    }
icculus@11750
  1020
slime73@11817
  1021
    /* We don't begin a new render pass right away - we delay it until an actual
slime73@11817
  1022
     * draw or clear happens. That way we can use hardware clears when possible,
slime73@11817
  1023
     * which are only available when beginning a new render pass. */
icculus@11729
  1024
    return 0;
slouken@11743
  1025
}}
icculus@11729
  1026
icculus@11729
  1027
static int
slime73@11789
  1028
METAL_SetOrthographicProjection(SDL_Renderer *renderer, int w, int h)
slime73@11789
  1029
{ @autoreleasepool {
slime73@11789
  1030
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slime73@11789
  1031
    float projection[4][4];
slime73@11789
  1032
slime73@11789
  1033
    if (!w || !h) {
slime73@11789
  1034
        return 0;
slime73@11789
  1035
    }
slime73@11789
  1036
slime73@11789
  1037
    /* Prepare an orthographic projection */
slime73@11789
  1038
    projection[0][0] = 2.0f / w;
slime73@11789
  1039
    projection[0][1] = 0.0f;
slime73@11789
  1040
    projection[0][2] = 0.0f;
slime73@11789
  1041
    projection[0][3] = 0.0f;
slime73@11789
  1042
    projection[1][0] = 0.0f;
slime73@11789
  1043
    projection[1][1] = -2.0f / h;
slime73@11789
  1044
    projection[1][2] = 0.0f;
slime73@11789
  1045
    projection[1][3] = 0.0f;
slime73@11789
  1046
    projection[2][0] = 0.0f;
slime73@11789
  1047
    projection[2][1] = 0.0f;
slime73@11789
  1048
    projection[2][2] = 0.0f;
slime73@11789
  1049
    projection[2][3] = 0.0f;
slime73@11789
  1050
    projection[3][0] = -1.0f;
slime73@11789
  1051
    projection[3][1] = 1.0f;
slime73@11789
  1052
    projection[3][2] = 0.0f;
slime73@11789
  1053
    projection[3][3] = 1.0f;
slime73@11789
  1054
slime73@11789
  1055
    // !!! FIXME: This should be in a buffer...
slime73@11789
  1056
    [data.mtlcmdencoder setVertexBytes:projection length:sizeof(float)*16 atIndex:2];
slime73@11789
  1057
    return 0;
slime73@11789
  1058
}}
slime73@11789
  1059
slime73@11789
  1060
static int
icculus@11729
  1061
METAL_UpdateViewport(SDL_Renderer * renderer)
slouken@11743
  1062
{ @autoreleasepool {
slouken@11732
  1063
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slime73@11817
  1064
    if (data.mtlcmdencoder) {
slime73@11817
  1065
        MTLViewport viewport;
slime73@11817
  1066
        viewport.originX = renderer->viewport.x;
slime73@11817
  1067
        viewport.originY = renderer->viewport.y;
slime73@11817
  1068
        viewport.width = renderer->viewport.w;
slime73@11817
  1069
        viewport.height = renderer->viewport.h;
slime73@11817
  1070
        viewport.znear = 0.0;
slime73@11817
  1071
        viewport.zfar = 1.0;
slime73@11817
  1072
        [data.mtlcmdencoder setViewport:viewport];
slime73@11817
  1073
        METAL_SetOrthographicProjection(renderer, renderer->viewport.w, renderer->viewport.h);
slime73@11817
  1074
    }
icculus@11729
  1075
    return 0;
slouken@11743
  1076
}}
icculus@11729
  1077
icculus@11729
  1078
static int
icculus@11729
  1079
METAL_UpdateClipRect(SDL_Renderer * renderer)
slouken@11743
  1080
{ @autoreleasepool {
slouken@11732
  1081
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slime73@11817
  1082
    if (data.mtlcmdencoder) {
slime73@11817
  1083
        MTLScissorRect mtlrect;
slime73@11817
  1084
        // !!! FIXME: should this care about the viewport?
slime73@11817
  1085
        if (renderer->clipping_enabled) {
slime73@11817
  1086
            const SDL_Rect *rect = &renderer->clip_rect;
slime73@11817
  1087
            mtlrect.x = renderer->viewport.x + rect->x;
slime73@11817
  1088
            mtlrect.y = renderer->viewport.x + rect->y;
slime73@11817
  1089
            mtlrect.width = rect->w;
slime73@11817
  1090
            mtlrect.height = rect->h;
slime73@11817
  1091
        } else {
slime73@11817
  1092
            mtlrect.x = renderer->viewport.x;
slime73@11817
  1093
            mtlrect.y = renderer->viewport.y;
slime73@11817
  1094
            mtlrect.width = renderer->viewport.w;
slime73@11817
  1095
            mtlrect.height = renderer->viewport.h;
slime73@11817
  1096
        }
slime73@11817
  1097
        if (mtlrect.width > 0 && mtlrect.height > 0) {
slime73@11817
  1098
            [data.mtlcmdencoder setScissorRect:mtlrect];
slime73@11817
  1099
        }
icculus@11729
  1100
    }
icculus@11729
  1101
    return 0;
slouken@11743
  1102
}}
icculus@11729
  1103
icculus@11729
  1104
static int
icculus@11729
  1105
METAL_RenderClear(SDL_Renderer * renderer)
slouken@11743
  1106
{ @autoreleasepool {
slouken@11732
  1107
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@11729
  1108
slime73@11817
  1109
    /* Since we set up the render command encoder lazily when a draw is
slime73@11817
  1110
     * requested, we can do the fast path hardware clear if no draws have
slime73@11817
  1111
     * happened since the last SetRenderTarget. */
slime73@11817
  1112
    if (data.mtlcmdencoder == nil) {
slime73@11817
  1113
        METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionClear);
slime73@11817
  1114
    } else {
slime73@11817
  1115
        // !!! FIXME: render color should live in a dedicated uniform buffer.
slime73@11817
  1116
        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
  1117
slime73@11817
  1118
        MTLViewport viewport;  // RenderClear ignores the viewport state, though, so reset that.
slime73@11817
  1119
        viewport.originX = viewport.originY = 0.0;
slime73@11817
  1120
        viewport.width = data.mtlpassdesc.colorAttachments[0].texture.width;
slime73@11817
  1121
        viewport.height = data.mtlpassdesc.colorAttachments[0].texture.height;
slime73@11817
  1122
        viewport.znear = 0.0;
slime73@11817
  1123
        viewport.zfar = 1.0;
icculus@11729
  1124
slime73@11817
  1125
        // Slow path for clearing: draw a filled fullscreen triangle.
slime73@11817
  1126
        METAL_SetOrthographicProjection(renderer, 1, 1);
slime73@11817
  1127
        [data.mtlcmdencoder setViewport:viewport];
slime73@11820
  1128
        [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, SDL_METAL_FRAGMENT_SOLID, SDL_BLENDMODE_NONE)];
slime73@11817
  1129
        [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_CLEAR_VERTS atIndex:0];
slime73@11817
  1130
        [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_IDENTITY atIndex:3];
slime73@11817
  1131
        [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
slime73@11817
  1132
        [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
icculus@11729
  1133
slime73@11817
  1134
        // reset the viewport for the rest of our usual drawing work...
slime73@11817
  1135
        viewport.originX = renderer->viewport.x;
slime73@11817
  1136
        viewport.originY = renderer->viewport.y;
slime73@11817
  1137
        viewport.width = renderer->viewport.w;
slime73@11817
  1138
        viewport.height = renderer->viewport.h;
slime73@11817
  1139
        viewport.znear = 0.0;
slime73@11817
  1140
        viewport.zfar = 1.0;
slime73@11817
  1141
        [data.mtlcmdencoder setViewport:viewport];
slime73@11817
  1142
        METAL_SetOrthographicProjection(renderer, renderer->viewport.w, renderer->viewport.h);
slime73@11817
  1143
    }
icculus@11729
  1144
icculus@11729
  1145
    return 0;
slouken@11743
  1146
}}
icculus@11729
  1147
slouken@11755
  1148
// normalize a value from 0.0f to len into 0.0f to 1.0f.
slouken@11755
  1149
static inline float
slouken@11755
  1150
normtex(const float _val, const float len)
slouken@11755
  1151
{
slime73@11810
  1152
    return _val / len;
slouken@11755
  1153
}
slouken@11755
  1154
icculus@11729
  1155
static int
icculus@11729
  1156
DrawVerts(SDL_Renderer * renderer, const SDL_FPoint * points, int count,
icculus@11729
  1157
          const MTLPrimitiveType primtype)
slouken@11743
  1158
{ @autoreleasepool {
slime73@11817
  1159
    METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
slouken@11735
  1160
slouken@11806
  1161
    const size_t vertlen = (sizeof (float) * 2) * count;
slouken@11732
  1162
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@11729
  1163
icculus@11729
  1164
    // !!! FIXME: render color should live in a dedicated uniform buffer.
icculus@11729
  1165
    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
  1166
slime73@11820
  1167
    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, SDL_METAL_FRAGMENT_SOLID, renderer->blendMode)];
slouken@11732
  1168
    [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
slouken@11806
  1169
slime73@11810
  1170
    [data.mtlcmdencoder setVertexBytes:points length:vertlen atIndex:0];
slime73@11810
  1171
    [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM atIndex:3];
slouken@11732
  1172
    [data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];
slouken@11806
  1173
icculus@11729
  1174
    return 0;
slouken@11743
  1175
}}
icculus@11729
  1176
icculus@11729
  1177
static int
icculus@11729
  1178
METAL_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points, int count)
icculus@11729
  1179
{
icculus@11729
  1180
    return DrawVerts(renderer, points, count, MTLPrimitiveTypePoint);
icculus@11729
  1181
}
icculus@11729
  1182
icculus@11729
  1183
static int
icculus@11729
  1184
METAL_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points, int count)
icculus@11729
  1185
{
icculus@11729
  1186
    return DrawVerts(renderer, points, count, MTLPrimitiveTypeLineStrip);
icculus@11729
  1187
}
icculus@11729
  1188
icculus@11729
  1189
static int
icculus@11729
  1190
METAL_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count)
slouken@11743
  1191
{ @autoreleasepool {
slime73@11817
  1192
    METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
slouken@11732
  1193
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@11729
  1194
icculus@11729
  1195
    // !!! FIXME: render color should live in a dedicated uniform buffer.
icculus@11729
  1196
    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
  1197
slime73@11820
  1198
    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, SDL_METAL_FRAGMENT_SOLID, renderer->blendMode)];
slouken@11732
  1199
    [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
slime73@11810
  1200
    [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_IDENTITY atIndex:3];
icculus@11729
  1201
icculus@11729
  1202
    for (int i = 0; i < count; i++, rects++) {
icculus@11729
  1203
        if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) continue;
icculus@11729
  1204
icculus@11729
  1205
        const float verts[] = {
slime73@11810
  1206
            rects->x, rects->y + rects->h,
slime73@11810
  1207
            rects->x, rects->y,
slime73@11810
  1208
            rects->x + rects->w, rects->y + rects->h,
slime73@11810
  1209
            rects->x + rects->w, rects->y
icculus@11729
  1210
        };
icculus@11729
  1211
slouken@11732
  1212
        [data.mtlcmdencoder setVertexBytes:verts length:sizeof(verts) atIndex:0];
slime73@11788
  1213
        [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
icculus@11729
  1214
    }
icculus@11729
  1215
icculus@11729
  1216
    return 0;
slouken@11743
  1217
}}
icculus@11729
  1218
slime73@11819
  1219
static void
slime73@11819
  1220
METAL_SetupRenderCopy(METAL_RenderData *data, SDL_Texture *texture, METAL_TextureData *texturedata)
slime73@11819
  1221
{
slime73@11819
  1222
    float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
slime73@11819
  1223
    if (texture->modMode) {
slime73@11819
  1224
        color[0] = ((float)texture->r) / 255.0f;
slime73@11819
  1225
        color[1] = ((float)texture->g) / 255.0f;
slime73@11819
  1226
        color[2] = ((float)texture->b) / 255.0f;
slime73@11819
  1227
        color[3] = ((float)texture->a) / 255.0f;
slime73@11819
  1228
    }
slime73@11819
  1229
slime73@11820
  1230
    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, texturedata.fragmentFunction, texture->blendMode)];
slime73@11819
  1231
    [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
slime73@11819
  1232
    [data.mtlcmdencoder setFragmentSamplerState:texturedata.mtlsampler atIndex:0];
slime73@11819
  1233
slime73@11819
  1234
    [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0];
slime73@11819
  1235
slime73@11819
  1236
    if (texturedata.yuv || texturedata.nv12) {
slime73@11819
  1237
        [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture_uv atIndex:1];
slime73@11819
  1238
        [data.mtlcmdencoder setFragmentBuffer:data.mtlbufconstants offset:texturedata.conversionBufferOffset atIndex:1];
slime73@11819
  1239
    }
slime73@11819
  1240
}
slime73@11819
  1241
icculus@11729
  1242
static int
icculus@11729
  1243
METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
  1244
              const SDL_Rect * srcrect, const SDL_FRect * dstrect)
slouken@11743
  1245
{ @autoreleasepool {
slime73@11817
  1246
    METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
slouken@11732
  1247
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slouken@11753
  1248
    METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
slouken@11753
  1249
    const float texw = (float) texturedata.mtltexture.width;
slouken@11753
  1250
    const float texh = (float) texturedata.mtltexture.height;
icculus@11729
  1251
slime73@11819
  1252
    METAL_SetupRenderCopy(data, texture, texturedata);
slime73@11819
  1253
icculus@11729
  1254
    const float xy[] = {
slime73@11810
  1255
        dstrect->x, dstrect->y + dstrect->h,
slime73@11810
  1256
        dstrect->x, dstrect->y,
slime73@11810
  1257
        dstrect->x + dstrect->w, dstrect->y + dstrect->h,
slime73@11810
  1258
        dstrect->x + dstrect->w, dstrect->y
icculus@11729
  1259
    };
icculus@11729
  1260
icculus@11729
  1261
    const float uv[] = {
slouken@11755
  1262
        normtex(srcrect->x, texw), normtex(srcrect->y + srcrect->h, texh),
slouken@11755
  1263
        normtex(srcrect->x, texw), normtex(srcrect->y, texh),
slime73@11788
  1264
        normtex(srcrect->x + srcrect->w, texw), normtex(srcrect->y + srcrect->h, texh),
slime73@11788
  1265
        normtex(srcrect->x + srcrect->w, texw), normtex(srcrect->y, texh)
icculus@11729
  1266
    };
icculus@11729
  1267
slime73@11799
  1268
    [data.mtlcmdencoder setVertexBytes:xy length:sizeof(xy) atIndex:0];
slime73@11799
  1269
    [data.mtlcmdencoder setVertexBytes:uv length:sizeof(uv) atIndex:1];
slime73@11810
  1270
    [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_IDENTITY atIndex:3];
slime73@11788
  1271
    [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
icculus@11729
  1272
icculus@11729
  1273
    return 0;
slouken@11743
  1274
}}
icculus@11729
  1275
icculus@11729
  1276
static int
icculus@11729
  1277
METAL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
icculus@11729
  1278
              const SDL_Rect * srcrect, const SDL_FRect * dstrect,
icculus@11729
  1279
              const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
slime73@11799
  1280
{ @autoreleasepool {
slime73@11817
  1281
    METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
slime73@11799
  1282
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slime73@11799
  1283
    METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
slime73@11799
  1284
    const float texw = (float) texturedata.mtltexture.width;
slime73@11799
  1285
    const float texh = (float) texturedata.mtltexture.height;
slime73@11799
  1286
    float transform[16];
slime73@11799
  1287
    float minu, maxu, minv, maxv;
slime73@11799
  1288
slime73@11819
  1289
    METAL_SetupRenderCopy(data, texture, texturedata);
slime73@11819
  1290
slime73@11799
  1291
    minu = normtex(srcrect->x, texw);
slime73@11799
  1292
    maxu = normtex(srcrect->x + srcrect->w, texw);
slime73@11799
  1293
    minv = normtex(srcrect->y, texh);
slime73@11799
  1294
    maxv = normtex(srcrect->y + srcrect->h, texh);
slime73@11799
  1295
slime73@11799
  1296
    if (flip & SDL_FLIP_HORIZONTAL) {
slime73@11799
  1297
        float tmp = maxu;
slime73@11799
  1298
        maxu = minu;
slime73@11799
  1299
        minu = tmp;
slime73@11799
  1300
    }
slime73@11799
  1301
    if (flip & SDL_FLIP_VERTICAL) {
slime73@11799
  1302
        float tmp = maxv;
slime73@11799
  1303
        maxv = minv;
slime73@11799
  1304
        minv = tmp;
slime73@11799
  1305
    }
slime73@11799
  1306
slime73@11799
  1307
    const float uv[] = {
slime73@11799
  1308
        minu, maxv,
slime73@11799
  1309
        minu, minv,
slime73@11799
  1310
        maxu, maxv,
slime73@11799
  1311
        maxu, minv
slime73@11799
  1312
    };
slime73@11799
  1313
slime73@11799
  1314
    const float xy[] = {
slime73@11810
  1315
        -center->x, dstrect->h - center->y,
slime73@11810
  1316
        -center->x, -center->y,
slime73@11810
  1317
        dstrect->w - center->x, dstrect->h - center->y,
slime73@11810
  1318
        dstrect->w - center->x, -center->y
slime73@11799
  1319
    };
slime73@11799
  1320
slime73@11799
  1321
    {
slime73@11799
  1322
        float rads = (float)(M_PI * (float) angle / 180.0f);
slime73@11799
  1323
        float c = cosf(rads), s = sinf(rads);
slime73@11799
  1324
        SDL_memset(transform, 0, sizeof(transform));
slime73@11799
  1325
slime73@11799
  1326
        transform[10] = transform[15] = 1.0f;
slime73@11821
  1327
slime73@11821
  1328
        /* Rotation */
slime73@11799
  1329
        transform[0]  = c;
slime73@11799
  1330
        transform[1]  = s;
slime73@11799
  1331
        transform[4]  = -s;
slime73@11799
  1332
        transform[5]  = c;
slime73@11821
  1333
slime73@11821
  1334
        /* Translation */
slime73@11799
  1335
        transform[12] = dstrect->x + center->x;
slime73@11799
  1336
        transform[13] = dstrect->y + center->y;
slime73@11799
  1337
    }
slime73@11799
  1338
slime73@11799
  1339
    [data.mtlcmdencoder setVertexBytes:xy length:sizeof(xy) atIndex:0];
slime73@11799
  1340
    [data.mtlcmdencoder setVertexBytes:uv length:sizeof(uv) atIndex:1];
slime73@11799
  1341
    [data.mtlcmdencoder setVertexBytes:transform length:sizeof(transform) atIndex:3];
slime73@11799
  1342
    [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
slime73@11799
  1343
slime73@11799
  1344
    return 0;
slime73@11799
  1345
}}
icculus@11729
  1346
icculus@11729
  1347
static int
icculus@11729
  1348
METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
icculus@11729
  1349
                    Uint32 pixel_format, void * pixels, int pitch)
slouken@11743
  1350
{ @autoreleasepool {
slime73@11817
  1351
    METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
slime73@11817
  1352
icculus@11749
  1353
    // !!! FIXME: this probably needs to commit the current command buffer, and probably waitUntilCompleted
slouken@11732
  1354
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slime73@11804
  1355
    id<MTLTexture> mtltexture = data.mtlpassdesc.colorAttachments[0].texture;
slime73@11800
  1356
    MTLRegion mtlregion = MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h);
icculus@11729
  1357
icculus@11729
  1358
    // we only do BGRA8 or RGBA8 at the moment, so 4 will do.
icculus@11729
  1359
    const int temp_pitch = rect->w * 4;
icculus@11729
  1360
    void *temp_pixels = SDL_malloc(temp_pitch * rect->h);
icculus@11729
  1361
    if (!temp_pixels) {
icculus@11729
  1362
        return SDL_OutOfMemory();
icculus@11729
  1363
    }
icculus@11729
  1364
icculus@11729
  1365
    [mtltexture getBytes:temp_pixels bytesPerRow:temp_pitch fromRegion:mtlregion mipmapLevel:0];
icculus@11729
  1366
icculus@11729
  1367
    const Uint32 temp_format = (mtltexture.pixelFormat == MTLPixelFormatBGRA8Unorm) ? SDL_PIXELFORMAT_ARGB8888 : SDL_PIXELFORMAT_ABGR8888;
icculus@11729
  1368
    const int status = SDL_ConvertPixels(rect->w, rect->h, temp_format, temp_pixels, temp_pitch, pixel_format, pixels, pitch);
icculus@11729
  1369
    SDL_free(temp_pixels);
icculus@11729
  1370
    return status;
slouken@11743
  1371
}}
icculus@11729
  1372
icculus@11729
  1373
static void
icculus@11729
  1374
METAL_RenderPresent(SDL_Renderer * renderer)
slouken@11743
  1375
{ @autoreleasepool {
slouken@11732
  1376
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
icculus@11729
  1377
slime73@11817
  1378
    if (data.mtlcmdencoder != nil) {
slime73@11817
  1379
        [data.mtlcmdencoder endEncoding];
slime73@11817
  1380
    }
slime73@11817
  1381
    if (data.mtlbackbuffer != nil) {
slime73@11817
  1382
        [data.mtlcmdbuffer presentDrawable:data.mtlbackbuffer];
slime73@11817
  1383
    }
slime73@11817
  1384
    if (data.mtlcmdbuffer != nil) {
slime73@11817
  1385
        [data.mtlcmdbuffer commit];
slime73@11817
  1386
    }
slouken@11741
  1387
    data.mtlcmdencoder = nil;
slouken@11741
  1388
    data.mtlcmdbuffer = nil;
slouken@11741
  1389
    data.mtlbackbuffer = nil;
slouken@11743
  1390
}}
icculus@11729
  1391
icculus@11729
  1392
static void
icculus@11729
  1393
METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
slouken@11743
  1394
{ @autoreleasepool {
slime73@11804
  1395
    CFBridgingRelease(texture->driverdata);
icculus@11729
  1396
    texture->driverdata = NULL;
slouken@11743
  1397
}}
icculus@11729
  1398
icculus@11729
  1399
static void
icculus@11729
  1400
METAL_DestroyRenderer(SDL_Renderer * renderer)
slouken@11743
  1401
{ @autoreleasepool {
slouken@11732
  1402
    if (renderer->driverdata) {
slouken@11732
  1403
        METAL_RenderData *data = CFBridgingRelease(renderer->driverdata);
icculus@11729
  1404
slouken@11741
  1405
        if (data.mtlcmdencoder != nil) {
slouken@11741
  1406
            [data.mtlcmdencoder endEncoding];
slouken@11741
  1407
        }
icculus@11746
  1408
slime73@11820
  1409
        DestroyAllPipelines(data.allpipelines, data.pipelinescount);
icculus@11729
  1410
    }
icculus@11746
  1411
icculus@11729
  1412
    SDL_free(renderer);
slouken@11743
  1413
}}
icculus@11729
  1414
slime73@11804
  1415
static void *
slime73@11804
  1416
METAL_GetMetalLayer(SDL_Renderer * renderer)
slouken@11744
  1417
{ @autoreleasepool {
slouken@11744
  1418
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slouken@11744
  1419
    return (__bridge void*)data.mtllayer;
slouken@11744
  1420
}}
slouken@11744
  1421
slime73@11804
  1422
static void *
slime73@11804
  1423
METAL_GetMetalCommandEncoder(SDL_Renderer * renderer)
slouken@11744
  1424
{ @autoreleasepool {
slime73@11817
  1425
    METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
slouken@11744
  1426
    METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
slouken@11744
  1427
    return (__bridge void*)data.mtlcmdencoder;
slouken@11744
  1428
}}
slouken@11744
  1429
icculus@11729
  1430
#endif /* SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED */
icculus@11729
  1431
icculus@11729
  1432
/* vi: set ts=4 sw=4 expandtab: */