src/render/metal/SDL_render_metal.m
changeset 11730 ac6c607e065c
parent 11729 d1ce8396c356
child 11732 ad13456d6e7f
equal deleted inserted replaced
11729:d1ce8396c356 11730:ac6c607e065c
     1 /*
     1 /*
     2   Simple DirectMedia Layer
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2015 Sam Lantinga <slouken@libsdl.org>
     3   Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
     4 
     4 
     5   This software is provided 'as-is', without any express or implied
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     7   arising from the use of this software.
     8 
     8 
    30 
    30 
    31 #include <Cocoa/Cocoa.h>
    31 #include <Cocoa/Cocoa.h>
    32 #include <Metal/Metal.h>
    32 #include <Metal/Metal.h>
    33 #include <QuartzCore/CAMetalLayer.h>
    33 #include <QuartzCore/CAMetalLayer.h>
    34 
    34 
    35 // these are in SDL_shaders_metal.c, regenerate it with build-metal-shaders.sh
    35 /* Regenerate these with build-metal-shaders.sh */
    36 extern const unsigned char sdl_metallib[];
    36 #ifdef __MACOSX__
    37 extern const unsigned int sdl_metallib_len;
    37 #include "SDL_shaders_metal_osx.h"
    38 
    38 #else
       
    39 #include "SDL_shaders_metal_ios.h"
       
    40 #endif
    39 
    41 
    40 /* Apple Metal renderer implementation */
    42 /* Apple Metal renderer implementation */
    41 
    43 
    42 static SDL_Renderer *METAL_CreateRenderer(SDL_Window * window, Uint32 flags);
    44 static SDL_Renderer *METAL_CreateRenderer(SDL_Window * window, Uint32 flags);
    43 static void METAL_WindowEvent(SDL_Renderer * renderer,
    45 static void METAL_WindowEvent(SDL_Renderer * renderer,
   139     mtlpipedesc.vertexFunction = mtlvertfn;
   141     mtlpipedesc.vertexFunction = mtlvertfn;
   140     mtlpipedesc.fragmentFunction = mtlfragfn;
   142     mtlpipedesc.fragmentFunction = mtlfragfn;
   141     mtlpipedesc.colorAttachments[0].pixelFormat = data->mtlbackbuffer.texture.pixelFormat;
   143     mtlpipedesc.colorAttachments[0].pixelFormat = data->mtlbackbuffer.texture.pixelFormat;
   142 
   144 
   143     switch (blendmode) {
   145     switch (blendmode) {
   144         case SDL_BLENDMODE_NONE:
       
   145             mtlpipedesc.colorAttachments[0].blendingEnabled = NO;
       
   146             break;
       
   147 
       
   148         case SDL_BLENDMODE_BLEND:
   146         case SDL_BLENDMODE_BLEND:
   149             mtlpipedesc.colorAttachments[0].blendingEnabled = YES;
   147             mtlpipedesc.colorAttachments[0].blendingEnabled = YES;
   150             mtlpipedesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
   148             mtlpipedesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
   151             mtlpipedesc.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
   149             mtlpipedesc.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
   152             mtlpipedesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
   150             mtlpipedesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
   172             mtlpipedesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorZero;
   170             mtlpipedesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorZero;
   173             mtlpipedesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorSourceColor;
   171             mtlpipedesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorSourceColor;
   174             mtlpipedesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorZero;
   172             mtlpipedesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorZero;
   175             mtlpipedesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOne;
   173             mtlpipedesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOne;
   176             break;
   174             break;
       
   175 
       
   176         default:
       
   177             mtlpipedesc.colorAttachments[0].blendingEnabled = NO;
       
   178             break;
   177     }
   179     }
   178 
   180 
   179     mtlpipedesc.label = label;
   181     mtlpipedesc.label = label;
   180 
   182 
   181     NSError *err = nil;
   183     NSError *err = nil;
   202 
   204 
   203 static inline id<MTLRenderPipelineState>
   205 static inline id<MTLRenderPipelineState>
   204 ChoosePipelineState(id<MTLRenderPipelineState> *states, const SDL_BlendMode blendmode)
   206 ChoosePipelineState(id<MTLRenderPipelineState> *states, const SDL_BlendMode blendmode)
   205 {
   207 {
   206     switch (blendmode) {
   208     switch (blendmode) {
   207         case SDL_BLENDMODE_NONE: return states[0];
       
   208         case SDL_BLENDMODE_BLEND: return states[1];
   209         case SDL_BLENDMODE_BLEND: return states[1];
   209         case SDL_BLENDMODE_ADD: return states[2];
   210         case SDL_BLENDMODE_ADD: return states[2];
   210         case SDL_BLENDMODE_MOD: return states[3];
   211         case SDL_BLENDMODE_MOD: return states[3];
       
   212         default: return states[0];
   211     }
   213     }
   212     return nil;
   214     return nil;
   213 }
   215 }
   214 
   216 
   215 static SDL_Renderer *
   217 static SDL_Renderer *
   242     }
   244     }
   243 
   245 
   244     renderer->driverdata = data;
   246     renderer->driverdata = data;
   245     renderer->window = window;
   247     renderer->window = window;
   246 
   248 
   247     data->mtldevice = MTLCreateSystemDefaultDevice();  // !!! FIXME: MTLCopyAllDevices() can find other GPUs...
   249 #ifdef __MACOSX__
   248     if (data->mtldevice == nil) {
   250     id<MTLDevice> mtldevice = MTLCreateSystemDefaultDevice();  // !!! FIXME: MTLCopyAllDevices() can find other GPUs...
       
   251     if (mtldevice == nil) {
   249         SDL_free(renderer);
   252         SDL_free(renderer);
   250         SDL_free(data);
   253         SDL_free(data);
   251         SDL_SetError("Failed to obtain Metal device");
   254         SDL_SetError("Failed to obtain Metal device");
   252         return NULL;
   255         return NULL;
   253     }
   256     }
   254 
   257 
   255     // !!! FIXME: error checking on all of this.
   258     // !!! FIXME: error checking on all of this.
   256 
   259 
   257     NSView *nsview = [syswm.info.cocoa.window contentView];
   260     NSView *nsview = [syswm.info.cocoa.window contentView];
   258 
   261 
   259     // !!! FIXME: on iOS, we need to override +[UIView layerClass] to return [CAMetalLayer class] right from the start, and that's more complicated.
   262     // CAMetalLayer is available in QuartzCore starting at OSX 10.11
   260     CAMetalLayer *layer = [CAMetalLayer layer];
   263     CAMetalLayer *layer = [NSClassFromString( @"CAMetalLayer" ) layer];
   261 
   264 
   262     layer.device = data->mtldevice;
   265     layer.device = mtldevice;
   263     //layer.pixelFormat = MTLPixelFormatBGRA8Unorm;  // !!! FIXME: MTLPixelFormatBGRA8Unorm_sRGB ?
   266     //layer.pixelFormat = MTLPixelFormatBGRA8Unorm;  // !!! FIXME: MTLPixelFormatBGRA8Unorm_sRGB ?
   264     layer.framebufferOnly = YES;
   267     layer.framebufferOnly = YES;
   265     //layer.drawableSize = (CGSize) [nsview convertRectToBacking:[nsview bounds]].size;
   268     //layer.drawableSize = (CGSize) [nsview convertRectToBacking:[nsview bounds]].size;
   266     //layer.colorspace = nil;
   269     //layer.colorspace = nil;
   267 
   270 
   268     [nsview setWantsLayer:YES];
   271     [nsview setWantsLayer:YES];
   269     [nsview setLayer:layer];
   272     [nsview setLayer:layer];
   270 
   273 
   271     [layer retain];
   274     [layer retain];
       
   275 #else
       
   276 
       
   277 #endif
       
   278 
       
   279     data->mtldevice = layer.device;
   272     data->mtllayer = layer;
   280     data->mtllayer = layer;
   273     data->mtlcmdqueue = [data->mtldevice newCommandQueue];
   281     data->mtlcmdqueue = [data->mtldevice newCommandQueue];
   274     data->mtlcmdqueue.label = @"SDL Metal Renderer";
   282     data->mtlcmdqueue.label = @"SDL Metal Renderer";
   275 
   283 
   276     data->mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor];  // !!! FIXME: is this autoreleased?
   284     data->mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor];  // !!! FIXME: is this autoreleased?
   316     // !!! FIXME: how do you control this in Metal?
   324     // !!! FIXME: how do you control this in Metal?
   317     renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
   325     renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
   318 
   326 
   319     NSError *err = nil;
   327     NSError *err = nil;
   320 
   328 
   321     // The compiled .metallib is embedded in a static array in SDL_shaders_metal.c,
   329     // The compiled .metallib is embedded in a static array in a header file
   322     //  but the original shader source code is in SDL_shaders_metal.metal.
   330     // but the original shader source code is in SDL_shaders_metal.metal.
   323     dispatch_data_t mtllibdata = dispatch_data_create(sdl_metallib, sdl_metallib_len, dispatch_get_global_queue(0, 0), ^{});
   331     dispatch_data_t mtllibdata = dispatch_data_create(sdl_metallib, sdl_metallib_len, dispatch_get_global_queue(0, 0), ^{});
   324     data->mtllibrary = [data->mtldevice newLibraryWithData:mtllibdata error:&err];
   332     data->mtllibrary = [data->mtldevice newLibraryWithData:mtllibdata error:&err];
   325     SDL_assert(err == nil);
   333     SDL_assert(err == nil);
   326     dispatch_release(mtllibdata);
   334     dispatch_release(mtllibdata);
   327     data->mtllibrary.label = @"SDL Metal renderer shader library";
   335     data->mtllibrary.label = @"SDL Metal renderer shader library";