metal: Fix pipeline states to use the pixel format of the current render target, instead of a hard-coded format.
authorAlex Szpakowski <slime73@gmail.com>
Sun, 07 Jan 2018 16:57:32 -0400
changeset 118203086a1942192
parent 11819 3b71fc7ed851
child 11821 43bba409e6d2
metal: Fix pipeline states to use the pixel format of the current render target, instead of a hard-coded format.
src/render/metal/SDL_render_metal.m
     1.1 --- a/src/render/metal/SDL_render_metal.m	Sat Jan 06 18:54:12 2018 -0400
     1.2 +++ b/src/render/metal/SDL_render_metal.m	Sun Jan 07 16:57:32 2018 -0400
     1.3 @@ -157,15 +157,19 @@
     1.4      int count;
     1.5      SDL_MetalVertexFunction vertexFunction;
     1.6      SDL_MetalFragmentFunction fragmentFunction;
     1.7 +    MTLPixelFormat renderTargetFormat;
     1.8      const char *label;
     1.9  } METAL_PipelineCache;
    1.10  
    1.11  /* Each shader combination used by drawing functions has a separate pipeline
    1.12 - * cache. This is more efficient than iterating over a global cache to find
    1.13 - * the pipeline based on the specified shader combination, since we know what
    1.14 - * the shader combination is inside each drawing function's code. */
    1.15 + * cache, and we have a separate list of caches for each render target pixel
    1.16 + * format. This is more efficient than iterating over a global cache to find
    1.17 + * the pipeline based on the specified shader combination and RT pixel format,
    1.18 + * since we know what the RT pixel format is when we set the render target, and
    1.19 + * we know what the shader combination is inside each drawing function's code. */
    1.20  typedef struct METAL_ShaderPipelines
    1.21  {
    1.22 +    MTLPixelFormat renderTargetFormat;
    1.23      METAL_PipelineCache caches[SDL_METAL_FRAGMENT_COUNT];
    1.24  } METAL_ShaderPipelines;
    1.25  
    1.26 @@ -181,7 +185,9 @@
    1.27      @property (nonatomic, retain) id<MTLBuffer> mtlbufconstants;
    1.28      @property (nonatomic, retain) CAMetalLayer *mtllayer;
    1.29      @property (nonatomic, retain) MTLRenderPassDescriptor *mtlpassdesc;
    1.30 -    @property (nonatomic, assign) METAL_ShaderPipelines *pipelines;
    1.31 +    @property (nonatomic, assign) METAL_ShaderPipelines *activepipelines;
    1.32 +    @property (nonatomic, assign) METAL_ShaderPipelines *allpipelines;
    1.33 +    @property (nonatomic, assign) int pipelinescount;
    1.34  @end
    1.35  
    1.36  @implementation METAL_RenderData
    1.37 @@ -315,8 +321,7 @@
    1.38  
    1.39      MTLRenderPipelineColorAttachmentDescriptor *rtdesc = mtlpipedesc.colorAttachments[0];
    1.40  
    1.41 -    // !!! FIXME: This should be part of the pipeline state cache.
    1.42 -    rtdesc.pixelFormat = data.mtllayer.pixelFormat;
    1.43 +    rtdesc.pixelFormat = cache->renderTargetFormat;
    1.44  
    1.45      if (blendmode != SDL_BLENDMODE_NONE) {
    1.46          rtdesc.blendingEnabled = YES;
    1.47 @@ -361,12 +366,14 @@
    1.48  }
    1.49  
    1.50  static void
    1.51 -MakePipelineCache(METAL_RenderData *data, METAL_PipelineCache *cache, const char *label, SDL_MetalVertexFunction vertfn, SDL_MetalFragmentFunction fragfn)
    1.52 +MakePipelineCache(METAL_RenderData *data, METAL_PipelineCache *cache, const char *label,
    1.53 +                  MTLPixelFormat rtformat, SDL_MetalVertexFunction vertfn, SDL_MetalFragmentFunction fragfn)
    1.54  {
    1.55      SDL_zerop(cache);
    1.56  
    1.57      cache->vertexFunction = vertfn;
    1.58      cache->fragmentFunction = fragfn;
    1.59 +    cache->renderTargetFormat = rtformat;
    1.60      cache->label = label;
    1.61  
    1.62      /* Create pipeline states for the default blend modes. Custom blend modes
    1.63 @@ -389,33 +396,58 @@
    1.64      }
    1.65  }
    1.66  
    1.67 +void
    1.68 +MakeShaderPipelines(METAL_RenderData *data, METAL_ShaderPipelines *pipelines, MTLPixelFormat rtformat)
    1.69 +{
    1.70 +    SDL_zerop(pipelines);
    1.71 +
    1.72 +    pipelines->renderTargetFormat = rtformat;
    1.73 +
    1.74 +    MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_SOLID], "SDL primitives pipeline", rtformat, SDL_METAL_VERTEX_SOLID, SDL_METAL_FRAGMENT_SOLID);
    1.75 +    MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_COPY], "SDL copy pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_COPY);
    1.76 +    MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_YUV], "SDL YUV pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_YUV);
    1.77 +    MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_NV12], "SDL NV12 pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_NV12);
    1.78 +    MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_NV21], "SDL NV21 pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_NV21);
    1.79 +}
    1.80 +
    1.81  static METAL_ShaderPipelines *
    1.82 -MakeShaderPipelines(METAL_RenderData *data)
    1.83 +ChooseShaderPipelines(METAL_RenderData *data, MTLPixelFormat rtformat)
    1.84  {
    1.85 -    METAL_ShaderPipelines *pipelines = SDL_calloc(1, sizeof(METAL_ShaderPipelines));
    1.86 -    if (!pipelines) {
    1.87 +    METAL_ShaderPipelines *allpipelines = data.allpipelines;
    1.88 +    int count = data.pipelinescount;
    1.89 +
    1.90 +    for (int i = 0; i < count; i++) {
    1.91 +        if (allpipelines[i].renderTargetFormat == rtformat) {
    1.92 +            return &allpipelines[i];
    1.93 +        }
    1.94 +    }
    1.95 +
    1.96 +    allpipelines = SDL_realloc(allpipelines, (count + 1) * sizeof(METAL_ShaderPipelines));
    1.97 +
    1.98 +    if (allpipelines == NULL) {
    1.99          SDL_OutOfMemory();
   1.100          return NULL;
   1.101      }
   1.102  
   1.103 -    MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_SOLID], "SDL primitives pipeline", SDL_METAL_VERTEX_SOLID, SDL_METAL_FRAGMENT_SOLID);
   1.104 -    MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_COPY], "SDL copy pipeline", SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_COPY);
   1.105 -    MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_YUV], "SDL YUV pipeline", SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_YUV);
   1.106 -    MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_NV12], "SDL NV12 pipeline", SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_NV12);
   1.107 -    MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_NV21], "SDL NV21 pipeline", SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_NV21);
   1.108 +    MakeShaderPipelines(data, &allpipelines[count], rtformat);
   1.109  
   1.110 -    return pipelines;
   1.111 +    data.allpipelines = allpipelines;
   1.112 +    data.pipelinescount = count + 1;
   1.113 +
   1.114 +    return &data.allpipelines[count];
   1.115  }
   1.116  
   1.117  static void
   1.118 -DestroyShaderPipelines(METAL_ShaderPipelines *pipelines)
   1.119 +DestroyAllPipelines(METAL_ShaderPipelines *allpipelines, int count)
   1.120  {
   1.121 -    if (pipelines != NULL) {
   1.122 -        for (int i = 0; i < SDL_METAL_FRAGMENT_COUNT; i++) {
   1.123 -            DestroyPipelineCache(&pipelines->caches[i]);
   1.124 +    if (allpipelines != NULL) {
   1.125 +        for (int i = 0; i < count; i++) {
   1.126 +            for (int cache = 0; cache < SDL_METAL_FRAGMENT_COUNT; cache++) {
   1.127 +                DestroyPipelineCache(&allpipelines[i].caches[cache]);
   1.128 +            }
   1.129          }
   1.130  
   1.131 -        SDL_free(pipelines);
   1.132 +        SDL_free(allpipelines);
   1.133      }
   1.134  }
   1.135  
   1.136 @@ -508,7 +540,10 @@
   1.137  #endif
   1.138      data.mtllibrary.label = @"SDL Metal renderer shader library";
   1.139  
   1.140 -    data.pipelines = MakeShaderPipelines(data);
   1.141 +    /* Do some shader pipeline state loading up-front rather than on demand. */
   1.142 +    data.pipelinescount = 0;
   1.143 +    data.allpipelines = NULL;
   1.144 +    ChooseShaderPipelines(data, MTLPixelFormatBGRA8Unorm);
   1.145  
   1.146      MTLSamplerDescriptor *samplerdesc = [[MTLSamplerDescriptor alloc] init];
   1.147  
   1.148 @@ -683,6 +718,8 @@
   1.149              data.mtlcmdencoder.label = @"SDL metal renderer render target";
   1.150          }
   1.151  
   1.152 +        data.activepipelines = ChooseShaderPipelines(data, mtltexture.pixelFormat);
   1.153 +
   1.154          /* Make sure the viewport and clip rect are set on the new render pass. */
   1.155          METAL_UpdateViewport(renderer);
   1.156          METAL_UpdateClipRect(renderer);
   1.157 @@ -844,11 +881,9 @@
   1.158  { @autoreleasepool {
   1.159      METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
   1.160  
   1.161 -    // !!! FIXME: this is a synchronous call; it doesn't return until data is uploaded in some form.
   1.162 -    // !!! FIXME:  Maybe move this off to a thread that marks the texture as uploaded and only stall the main thread if we try to
   1.163 -    // !!! FIXME:  use this texture before the marking is done? Is it worth it? Or will we basically always be uploading a bunch of
   1.164 -    // !!! FIXME:  stuff way ahead of time and/or using it immediately after upload?
   1.165 -
   1.166 +    /* !!! FIXME: replaceRegion does not do any synchronization, so it might
   1.167 +     * !!! FIXME: stomp on a previous frame's data that's currently being read
   1.168 +     * !!! FIXME: by the GPU. */
   1.169      [texturedata.mtltexture replaceRegion:MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h)
   1.170                                mipmapLevel:0
   1.171                                  withBytes:pixels
   1.172 @@ -1064,7 +1099,7 @@
   1.173          // Slow path for clearing: draw a filled fullscreen triangle.
   1.174          METAL_SetOrthographicProjection(renderer, 1, 1);
   1.175          [data.mtlcmdencoder setViewport:viewport];
   1.176 -        [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.pipelines, SDL_METAL_FRAGMENT_SOLID, SDL_BLENDMODE_NONE)];
   1.177 +        [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, SDL_METAL_FRAGMENT_SOLID, SDL_BLENDMODE_NONE)];
   1.178          [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_CLEAR_VERTS atIndex:0];
   1.179          [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_IDENTITY atIndex:3];
   1.180          [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
   1.181 @@ -1103,7 +1138,7 @@
   1.182      // !!! FIXME: render color should live in a dedicated uniform buffer.
   1.183      const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
   1.184  
   1.185 -    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.pipelines, SDL_METAL_FRAGMENT_SOLID, renderer->blendMode)];
   1.186 +    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, SDL_METAL_FRAGMENT_SOLID, renderer->blendMode)];
   1.187      [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
   1.188  
   1.189      [data.mtlcmdencoder setVertexBytes:points length:vertlen atIndex:0];
   1.190 @@ -1134,7 +1169,7 @@
   1.191      // !!! FIXME: render color should live in a dedicated uniform buffer.
   1.192      const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
   1.193  
   1.194 -    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.pipelines, SDL_METAL_FRAGMENT_SOLID, renderer->blendMode)];
   1.195 +    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, SDL_METAL_FRAGMENT_SOLID, renderer->blendMode)];
   1.196      [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
   1.197      [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_IDENTITY atIndex:3];
   1.198  
   1.199 @@ -1166,7 +1201,7 @@
   1.200          color[3] = ((float)texture->a) / 255.0f;
   1.201      }
   1.202  
   1.203 -    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.pipelines, texturedata.fragmentFunction, texture->blendMode)];
   1.204 +    [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, texturedata.fragmentFunction, texture->blendMode)];
   1.205      [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
   1.206      [data.mtlcmdencoder setFragmentSamplerState:texturedata.mtlsampler atIndex:0];
   1.207  
   1.208 @@ -1347,7 +1382,7 @@
   1.209              [data.mtlcmdencoder endEncoding];
   1.210          }
   1.211  
   1.212 -        DestroyShaderPipelines(data.pipelines);
   1.213 +        DestroyAllPipelines(data.allpipelines, data.pipelinescount);
   1.214      }
   1.215  
   1.216      SDL_free(renderer);