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