src/render/metal/SDL_render_metal.m
branchSDL-ryan-batching-renderer
changeset 12213 e4f5271ec8cc
parent 12179 c06be57c5a01
child 12215 985191d8125c
equal deleted inserted replaced
12212:e5a6ed2f98a3 12213:e4f5271ec8cc
    62                                const Uint8 *Vplane, int Vpitch);
    62                                const Uint8 *Vplane, int Vpitch);
    63 static int METAL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    63 static int METAL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
    64                           const SDL_Rect * rect, void **pixels, int *pitch);
    64                           const SDL_Rect * rect, void **pixels, int *pitch);
    65 static void METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    65 static void METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    66 static int METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
    66 static int METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
    67 static int METAL_UpdateViewport(SDL_Renderer * renderer);
    67 static int METAL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points,
    68 static int METAL_UpdateClipRect(SDL_Renderer * renderer);
    68                              int count);
    69 static int METAL_RenderClear(SDL_Renderer * renderer);
    69 static int METAL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects,
    70 static int METAL_RenderDrawPoints(SDL_Renderer * renderer,
    70                             int count);
    71                                const SDL_FPoint * points, int count);
    71 static int METAL_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
    72 static int METAL_RenderDrawLines(SDL_Renderer * renderer,
    72                        const SDL_Rect * srcrect, const SDL_FRect * dstrect);
    73                               const SDL_FPoint * points, int count);
    73 static int METAL_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
    74 static int METAL_RenderFillRects(SDL_Renderer * renderer,
    74                         const SDL_Rect * srcquad, const SDL_FRect * dstrect,
    75                               const SDL_FRect * rects, int count);
    75                         const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
    76 static int METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
    76 static int METAL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize);
    77                          const SDL_Rect * srcrect, const SDL_FRect * dstrect);
       
    78 static int METAL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
       
    79                          const SDL_Rect * srcrect, const SDL_FRect * dstrect,
       
    80                          const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
       
    81 static int METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
    77 static int METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
    82                                Uint32 pixel_format, void * pixels, int pitch);
    78                                Uint32 pixel_format, void * pixels, int pitch);
    83 static void METAL_RenderPresent(SDL_Renderer * renderer);
    79 static void METAL_RenderPresent(SDL_Renderer * renderer);
    84 static void METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    80 static void METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture);
    85 static void METAL_DestroyRenderer(SDL_Renderer * renderer);
    81 static void METAL_DestroyRenderer(SDL_Renderer * renderer);
   111 #define CONSTANT_ALIGN 4
   107 #define CONSTANT_ALIGN 4
   112 #endif
   108 #endif
   113 
   109 
   114 #define ALIGN_CONSTANTS(size) ((size + CONSTANT_ALIGN - 1) & (~(CONSTANT_ALIGN - 1)))
   110 #define ALIGN_CONSTANTS(size) ((size + CONSTANT_ALIGN - 1) & (~(CONSTANT_ALIGN - 1)))
   115 
   111 
       
   112 static const size_t CONSTANTS_OFFSET_INVALID = 0xFFFFFFFF;
   116 static const size_t CONSTANTS_OFFSET_IDENTITY = 0;
   113 static const size_t CONSTANTS_OFFSET_IDENTITY = 0;
   117 static const size_t CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM = ALIGN_CONSTANTS(CONSTANTS_OFFSET_IDENTITY + sizeof(float) * 16);
   114 static const size_t CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM = ALIGN_CONSTANTS(CONSTANTS_OFFSET_IDENTITY + sizeof(float) * 16);
   118 static const size_t CONSTANTS_OFFSET_DECODE_JPEG = ALIGN_CONSTANTS(CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM + sizeof(float) * 16);
   115 static const size_t CONSTANTS_OFFSET_DECODE_JPEG = ALIGN_CONSTANTS(CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM + sizeof(float) * 16);
   119 static const size_t CONSTANTS_OFFSET_DECODE_BT601 = ALIGN_CONSTANTS(CONSTANTS_OFFSET_DECODE_JPEG + sizeof(float) * 4 * 4);
   116 static const size_t CONSTANTS_OFFSET_DECODE_BT601 = ALIGN_CONSTANTS(CONSTANTS_OFFSET_DECODE_JPEG + sizeof(float) * 4 * 4);
   120 static const size_t CONSTANTS_OFFSET_DECODE_BT709 = ALIGN_CONSTANTS(CONSTANTS_OFFSET_DECODE_BT601 + sizeof(float) * 4 * 4);
   117 static const size_t CONSTANTS_OFFSET_DECODE_BT709 = ALIGN_CONSTANTS(CONSTANTS_OFFSET_DECODE_BT601 + sizeof(float) * 4 * 4);
   586     };
   583     };
   587 
   584 
   588     float clearverts[6] = {0.0f, 0.0f,  0.0f, 2.0f,  2.0f, 0.0f};
   585     float clearverts[6] = {0.0f, 0.0f,  0.0f, 2.0f,  2.0f, 0.0f};
   589 
   586 
   590     id<MTLBuffer> mtlbufconstantstaging = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModeShared];
   587     id<MTLBuffer> mtlbufconstantstaging = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModeShared];
       
   588     #if !__has_feature(objc_arc)
       
   589     [mtlbufconstantstaging autorelease];
       
   590     #endif
   591     mtlbufconstantstaging.label = @"SDL constant staging data";
   591     mtlbufconstantstaging.label = @"SDL constant staging data";
   592 
   592 
   593     id<MTLBuffer> mtlbufconstants = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModePrivate];
   593     id<MTLBuffer> mtlbufconstants = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModePrivate];
   594     data.mtlbufconstants = mtlbufconstants;
   594     data.mtlbufconstants = mtlbufconstants;
   595     data.mtlbufconstants.label = @"SDL constant data";
   595     data.mtlbufconstants.label = @"SDL constant data";
   619     renderer->UpdateTexture = METAL_UpdateTexture;
   619     renderer->UpdateTexture = METAL_UpdateTexture;
   620     renderer->UpdateTextureYUV = METAL_UpdateTextureYUV;
   620     renderer->UpdateTextureYUV = METAL_UpdateTextureYUV;
   621     renderer->LockTexture = METAL_LockTexture;
   621     renderer->LockTexture = METAL_LockTexture;
   622     renderer->UnlockTexture = METAL_UnlockTexture;
   622     renderer->UnlockTexture = METAL_UnlockTexture;
   623     renderer->SetRenderTarget = METAL_SetRenderTarget;
   623     renderer->SetRenderTarget = METAL_SetRenderTarget;
   624     renderer->UpdateViewport = METAL_UpdateViewport;
   624     renderer->QueueDrawPoints = METAL_QueueDrawPoints;
   625     renderer->UpdateClipRect = METAL_UpdateClipRect;
   625     renderer->QueueDrawLines = METAL_QueueDrawPoints;  // lines and points queue the same way.
   626     renderer->RenderClear = METAL_RenderClear;
   626     renderer->QueueFillRects = METAL_QueueFillRects;
   627     renderer->RenderDrawPoints = METAL_RenderDrawPoints;
   627     renderer->QueueCopy = METAL_QueueCopy;
   628     renderer->RenderDrawLines = METAL_RenderDrawLines;
   628     renderer->QueueCopyEx = METAL_QueueCopyEx;
   629     renderer->RenderFillRects = METAL_RenderFillRects;
   629     renderer->RunCommandQueue = METAL_RunCommandQueue;
   630     renderer->RenderCopy = METAL_RenderCopy;
       
   631     renderer->RenderCopyEx = METAL_RenderCopyEx;
       
   632     renderer->RenderReadPixels = METAL_RenderReadPixels;
   630     renderer->RenderReadPixels = METAL_RenderReadPixels;
   633     renderer->RenderPresent = METAL_RenderPresent;
   631     renderer->RenderPresent = METAL_RenderPresent;
   634     renderer->DestroyTexture = METAL_DestroyTexture;
   632     renderer->DestroyTexture = METAL_DestroyTexture;
   635     renderer->DestroyRenderer = METAL_DestroyRenderer;
   633     renderer->DestroyRenderer = METAL_DestroyRenderer;
   636     renderer->GetMetalLayer = METAL_GetMetalLayer;
   634     renderer->GetMetalLayer = METAL_GetMetalLayer;
   696 
   694 
   697     return renderer;
   695     return renderer;
   698 }}
   696 }}
   699 
   697 
   700 static void
   698 static void
   701 METAL_ActivateRenderCommandEncoder(SDL_Renderer * renderer, MTLLoadAction load)
   699 METAL_ActivateRenderCommandEncoder(SDL_Renderer * renderer, MTLLoadAction load, MTLClearColor *clear_color)
   702 {
   700 {
   703     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
   701     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
   704 
   702 
   705     /* Our SetRenderTarget just signals that the next render operation should
   703     /* Our SetRenderTarget just signals that the next render operation should
   706      * set up a new render pass. This is where that work happens. */
   704      * set up a new render pass. This is where that work happens. */
   723         }
   721         }
   724 
   722 
   725         SDL_assert(mtltexture);
   723         SDL_assert(mtltexture);
   726 
   724 
   727         if (load == MTLLoadActionClear) {
   725         if (load == MTLLoadActionClear) {
   728             MTLClearColor color = MTLClearColorMake(renderer->r/255.0, renderer->g/255.0, renderer->b/255.0, renderer->a/255.0);
   726             SDL_assert(clear_color != NULL);
   729             data.mtlpassdesc.colorAttachments[0].clearColor = color;
   727             data.mtlpassdesc.colorAttachments[0].clearColor = *clear_color;
   730         }
   728         }
   731 
   729 
   732         data.mtlpassdesc.colorAttachments[0].loadAction = load;
   730         data.mtlpassdesc.colorAttachments[0].loadAction = load;
   733         data.mtlpassdesc.colorAttachments[0].texture = mtltexture;
   731         data.mtlpassdesc.colorAttachments[0].texture = mtltexture;
   734 
   732 
   740         } else {
   738         } else {
   741             data.mtlcmdencoder.label = @"SDL metal renderer render target";
   739             data.mtlcmdencoder.label = @"SDL metal renderer render target";
   742         }
   740         }
   743 
   741 
   744         data.activepipelines = ChooseShaderPipelines(data, mtltexture.pixelFormat);
   742         data.activepipelines = ChooseShaderPipelines(data, mtltexture.pixelFormat);
   745 
       
   746         /* Make sure the viewport and clip rect are set on the new render pass. */
       
   747         METAL_UpdateViewport(renderer);
       
   748         METAL_UpdateClipRect(renderer);
       
   749     }
   743     }
   750 }
   744 }
   751 
   745 
   752 static void
   746 static void
   753 METAL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
   747 METAL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
  1022      * draw or clear happens. That way we can use hardware clears when possible,
  1016      * draw or clear happens. That way we can use hardware clears when possible,
  1023      * which are only available when beginning a new render pass. */
  1017      * which are only available when beginning a new render pass. */
  1024     return 0;
  1018     return 0;
  1025 }}
  1019 }}
  1026 
  1020 
  1027 static int
       
  1028 METAL_SetOrthographicProjection(SDL_Renderer *renderer, int w, int h)
       
  1029 { @autoreleasepool {
       
  1030     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
       
  1031     float projection[4][4];
       
  1032 
       
  1033     if (!w || !h) {
       
  1034         return 0;
       
  1035     }
       
  1036 
       
  1037     /* Prepare an orthographic projection */
       
  1038     projection[0][0] = 2.0f / w;
       
  1039     projection[0][1] = 0.0f;
       
  1040     projection[0][2] = 0.0f;
       
  1041     projection[0][3] = 0.0f;
       
  1042     projection[1][0] = 0.0f;
       
  1043     projection[1][1] = -2.0f / h;
       
  1044     projection[1][2] = 0.0f;
       
  1045     projection[1][3] = 0.0f;
       
  1046     projection[2][0] = 0.0f;
       
  1047     projection[2][1] = 0.0f;
       
  1048     projection[2][2] = 0.0f;
       
  1049     projection[2][3] = 0.0f;
       
  1050     projection[3][0] = -1.0f;
       
  1051     projection[3][1] = 1.0f;
       
  1052     projection[3][2] = 0.0f;
       
  1053     projection[3][3] = 1.0f;
       
  1054 
       
  1055     // !!! FIXME: This should be in a buffer...
       
  1056     [data.mtlcmdencoder setVertexBytes:projection length:sizeof(float)*16 atIndex:2];
       
  1057     return 0;
       
  1058 }}
       
  1059 
       
  1060 static int
       
  1061 METAL_UpdateViewport(SDL_Renderer * renderer)
       
  1062 { @autoreleasepool {
       
  1063     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
       
  1064     if (data.mtlcmdencoder) {
       
  1065         MTLViewport viewport;
       
  1066         viewport.originX = renderer->viewport.x;
       
  1067         viewport.originY = renderer->viewport.y;
       
  1068         viewport.width = renderer->viewport.w;
       
  1069         viewport.height = renderer->viewport.h;
       
  1070         viewport.znear = 0.0;
       
  1071         viewport.zfar = 1.0;
       
  1072         [data.mtlcmdencoder setViewport:viewport];
       
  1073         METAL_SetOrthographicProjection(renderer, renderer->viewport.w, renderer->viewport.h);
       
  1074     }
       
  1075     return 0;
       
  1076 }}
       
  1077 
       
  1078 static int
       
  1079 METAL_UpdateClipRect(SDL_Renderer * renderer)
       
  1080 { @autoreleasepool {
       
  1081     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
       
  1082     if (data.mtlcmdencoder) {
       
  1083         MTLScissorRect mtlrect;
       
  1084         // !!! FIXME: should this care about the viewport?
       
  1085         if (renderer->clipping_enabled) {
       
  1086             const SDL_Rect *rect = &renderer->clip_rect;
       
  1087             mtlrect.x = renderer->viewport.x + rect->x;
       
  1088             mtlrect.y = renderer->viewport.x + rect->y;
       
  1089             mtlrect.width = rect->w;
       
  1090             mtlrect.height = rect->h;
       
  1091         } else {
       
  1092             mtlrect.x = renderer->viewport.x;
       
  1093             mtlrect.y = renderer->viewport.y;
       
  1094             mtlrect.width = renderer->viewport.w;
       
  1095             mtlrect.height = renderer->viewport.h;
       
  1096         }
       
  1097         if (mtlrect.width > 0 && mtlrect.height > 0) {
       
  1098             [data.mtlcmdencoder setScissorRect:mtlrect];
       
  1099         }
       
  1100     }
       
  1101     return 0;
       
  1102 }}
       
  1103 
       
  1104 static int
       
  1105 METAL_RenderClear(SDL_Renderer * renderer)
       
  1106 { @autoreleasepool {
       
  1107     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
       
  1108 
       
  1109     /* Since we set up the render command encoder lazily when a draw is
       
  1110      * requested, we can do the fast path hardware clear if no draws have
       
  1111      * happened since the last SetRenderTarget. */
       
  1112     if (data.mtlcmdencoder == nil) {
       
  1113         METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionClear);
       
  1114     } else {
       
  1115         // !!! FIXME: render color should live in a dedicated uniform buffer.
       
  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 };
       
  1117 
       
  1118         MTLViewport viewport;  // RenderClear ignores the viewport state, though, so reset that.
       
  1119         viewport.originX = viewport.originY = 0.0;
       
  1120         viewport.width = data.mtlpassdesc.colorAttachments[0].texture.width;
       
  1121         viewport.height = data.mtlpassdesc.colorAttachments[0].texture.height;
       
  1122         viewport.znear = 0.0;
       
  1123         viewport.zfar = 1.0;
       
  1124 
       
  1125         // Slow path for clearing: draw a filled fullscreen triangle.
       
  1126         METAL_SetOrthographicProjection(renderer, 1, 1);
       
  1127         [data.mtlcmdencoder setViewport:viewport];
       
  1128         [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, SDL_METAL_FRAGMENT_SOLID, SDL_BLENDMODE_NONE)];
       
  1129         [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_CLEAR_VERTS atIndex:0];
       
  1130         [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_IDENTITY atIndex:3];
       
  1131         [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
       
  1132         [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
       
  1133 
       
  1134         // reset the viewport for the rest of our usual drawing work...
       
  1135         viewport.originX = renderer->viewport.x;
       
  1136         viewport.originY = renderer->viewport.y;
       
  1137         viewport.width = renderer->viewport.w;
       
  1138         viewport.height = renderer->viewport.h;
       
  1139         viewport.znear = 0.0;
       
  1140         viewport.zfar = 1.0;
       
  1141         [data.mtlcmdencoder setViewport:viewport];
       
  1142         METAL_SetOrthographicProjection(renderer, renderer->viewport.w, renderer->viewport.h);
       
  1143     }
       
  1144 
       
  1145     return 0;
       
  1146 }}
       
  1147 
  1021 
  1148 // normalize a value from 0.0f to len into 0.0f to 1.0f.
  1022 // normalize a value from 0.0f to len into 0.0f to 1.0f.
  1149 static inline float
  1023 static inline float
  1150 normtex(const float _val, const float len)
  1024 normtex(const float _val, const float len)
  1151 {
  1025 {
  1152     return _val / len;
  1026     return _val / len;
  1153 }
  1027 }
  1154 
  1028 
  1155 static int
  1029 static int
  1156 DrawVerts(SDL_Renderer * renderer, const SDL_FPoint * points, int count,
  1030 METAL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count)
  1157           const MTLPrimitiveType primtype)
  1031 {
  1158 { @autoreleasepool {
       
  1159     METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
       
  1160 
       
  1161     const size_t vertlen = (sizeof (float) * 2) * count;
  1032     const size_t vertlen = (sizeof (float) * 2) * count;
  1162     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
  1033     float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, &cmd->data.draw.first);
  1163 
  1034     if (!verts) {
  1164     // !!! FIXME: render color should live in a dedicated uniform buffer.
  1035         return -1;
  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 };
  1036     }
  1166 
  1037     cmd->data.draw.count = count;
  1167     [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, SDL_METAL_FRAGMENT_SOLID, renderer->blendMode)];
  1038     SDL_memcpy(verts, points, vertlen);
  1168     [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
       
  1169 
       
  1170     [data.mtlcmdencoder setVertexBytes:points length:vertlen atIndex:0];
       
  1171     [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM atIndex:3];
       
  1172     [data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];
       
  1173 
       
  1174     return 0;
  1039     return 0;
  1175 }}
  1040 }
  1176 
  1041 
  1177 static int
  1042 static int
  1178 METAL_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points, int count)
  1043 METAL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count)
  1179 {
  1044 {
  1180     return DrawVerts(renderer, points, count, MTLPrimitiveTypePoint);
  1045     // !!! FIXME: use an index buffer
       
  1046     const size_t vertlen = (sizeof (float) * 8) * count;
       
  1047     float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, &cmd->data.draw.first);
       
  1048     if (!verts) {
       
  1049         return -1;
       
  1050     }
       
  1051 
       
  1052     cmd->data.draw.count = count;
       
  1053 
       
  1054     for (int i = 0; i < count; i++, rects++) {
       
  1055         if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) {
       
  1056             cmd->data.draw.count--;
       
  1057         } else {
       
  1058             *(verts++) = rects->x;
       
  1059             *(verts++) = rects->y + rects->h;
       
  1060             *(verts++) = rects->x;
       
  1061             *(verts++) = rects->y;
       
  1062             *(verts++) = rects->x + rects->w;
       
  1063             *(verts++) = rects->y + rects->h;
       
  1064             *(verts++) = rects->x + rects->w;
       
  1065             *(verts++) = rects->y;
       
  1066         }
       
  1067     }
       
  1068 
       
  1069     if (cmd->data.draw.count == 0) {
       
  1070         cmd->command = SDL_RENDERCMD_NO_OP;  // nothing to do, just skip this one later.
       
  1071     }
       
  1072 
       
  1073     return 0;
  1181 }
  1074 }
  1182 
  1075 
  1183 static int
  1076 static int
  1184 METAL_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points, int count)
  1077 METAL_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
  1185 {
  1078                 const SDL_Rect * srcrect, const SDL_FRect * dstrect)
  1186     return DrawVerts(renderer, points, count, MTLPrimitiveTypeLineStrip);
  1079 {
  1187 }
       
  1188 
       
  1189 static int
       
  1190 METAL_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count)
       
  1191 { @autoreleasepool {
       
  1192     METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
       
  1193     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
       
  1194 
       
  1195     // !!! FIXME: render color should live in a dedicated uniform buffer.
       
  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 };
       
  1197 
       
  1198     [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, SDL_METAL_FRAGMENT_SOLID, renderer->blendMode)];
       
  1199     [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
       
  1200     [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_IDENTITY atIndex:3];
       
  1201 
       
  1202     for (int i = 0; i < count; i++, rects++) {
       
  1203         if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) continue;
       
  1204 
       
  1205         const float verts[] = {
       
  1206             rects->x, rects->y + rects->h,
       
  1207             rects->x, rects->y,
       
  1208             rects->x + rects->w, rects->y + rects->h,
       
  1209             rects->x + rects->w, rects->y
       
  1210         };
       
  1211 
       
  1212         [data.mtlcmdencoder setVertexBytes:verts length:sizeof(verts) atIndex:0];
       
  1213         [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
       
  1214     }
       
  1215 
       
  1216     return 0;
       
  1217 }}
       
  1218 
       
  1219 static void
       
  1220 METAL_SetupRenderCopy(METAL_RenderData *data, SDL_Texture *texture, METAL_TextureData *texturedata)
       
  1221 {
       
  1222     float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
       
  1223     if (texture->modMode) {
       
  1224         color[0] = ((float)texture->r) / 255.0f;
       
  1225         color[1] = ((float)texture->g) / 255.0f;
       
  1226         color[2] = ((float)texture->b) / 255.0f;
       
  1227         color[3] = ((float)texture->a) / 255.0f;
       
  1228     }
       
  1229 
       
  1230     [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.activepipelines, texturedata.fragmentFunction, texture->blendMode)];
       
  1231     [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
       
  1232     [data.mtlcmdencoder setFragmentSamplerState:texturedata.mtlsampler atIndex:0];
       
  1233 
       
  1234     [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0];
       
  1235 
       
  1236     if (texturedata.yuv || texturedata.nv12) {
       
  1237         [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture_uv atIndex:1];
       
  1238         [data.mtlcmdencoder setFragmentBuffer:data.mtlbufconstants offset:texturedata.conversionBufferOffset atIndex:1];
       
  1239     }
       
  1240 }
       
  1241 
       
  1242 static int
       
  1243 METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
       
  1244               const SDL_Rect * srcrect, const SDL_FRect * dstrect)
       
  1245 { @autoreleasepool {
       
  1246     METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
       
  1247     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
       
  1248     METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
  1080     METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
  1249     const float texw = (float) texturedata.mtltexture.width;
  1081     const float texw = (float) texturedata.mtltexture.width;
  1250     const float texh = (float) texturedata.mtltexture.height;
  1082     const float texh = (float) texturedata.mtltexture.height;
  1251 
  1083     // !!! FIXME: use an index buffer
  1252     METAL_SetupRenderCopy(data, texture, texturedata);
  1084     const size_t vertlen = (sizeof (float) * 16);
  1253 
  1085     float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, &cmd->data.draw.first);
  1254     const float xy[] = {
  1086     if (!verts) {
  1255         dstrect->x, dstrect->y + dstrect->h,
  1087         return -1;
  1256         dstrect->x, dstrect->y,
  1088     }
  1257         dstrect->x + dstrect->w, dstrect->y + dstrect->h,
  1089 
  1258         dstrect->x + dstrect->w, dstrect->y
  1090     cmd->data.draw.count = 1;
  1259     };
  1091 
  1260 
  1092     *(verts++) = dstrect->x;
  1261     const float uv[] = {
  1093     *(verts++) = dstrect->y + dstrect->h;
  1262         normtex(srcrect->x, texw), normtex(srcrect->y + srcrect->h, texh),
  1094     *(verts++) = dstrect->x;
  1263         normtex(srcrect->x, texw), normtex(srcrect->y, texh),
  1095     *(verts++) = dstrect->y;
  1264         normtex(srcrect->x + srcrect->w, texw), normtex(srcrect->y + srcrect->h, texh),
  1096     *(verts++) = dstrect->x + dstrect->w;
  1265         normtex(srcrect->x + srcrect->w, texw), normtex(srcrect->y, texh)
  1097     *(verts++) = dstrect->y + dstrect->h;
  1266     };
  1098     *(verts++) = dstrect->x + dstrect->w;
  1267 
  1099     *(verts++) = dstrect->y;
  1268     [data.mtlcmdencoder setVertexBytes:xy length:sizeof(xy) atIndex:0];
  1100 
  1269     [data.mtlcmdencoder setVertexBytes:uv length:sizeof(uv) atIndex:1];
  1101     *(verts++) = normtex(srcrect->x, texw);
  1270     [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_IDENTITY atIndex:3];
  1102     *(verts++) = normtex(srcrect->y + srcrect->h, texh);
  1271     [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
  1103     *(verts++) = normtex(srcrect->x, texw);
       
  1104     *(verts++) = normtex(srcrect->y, texh);
       
  1105     *(verts++) = normtex(srcrect->x + srcrect->w, texw);
       
  1106     *(verts++) = normtex(srcrect->y + srcrect->h, texh);
       
  1107     *(verts++) = normtex(srcrect->x + srcrect->w, texw);
       
  1108     *(verts++) = normtex(srcrect->y, texh);
  1272 
  1109 
  1273     return 0;
  1110     return 0;
  1274 }}
  1111 }
  1275 
  1112 
  1276 static int
  1113 static int
  1277 METAL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
  1114 METAL_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
  1278               const SDL_Rect * srcrect, const SDL_FRect * dstrect,
  1115                   const SDL_Rect * srcquad, const SDL_FRect * dstrect,
  1279               const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
  1116                   const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
  1280 { @autoreleasepool {
  1117 {
  1281     METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
       
  1282     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
       
  1283     METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
  1118     METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
  1284     const float texw = (float) texturedata.mtltexture.width;
  1119     const float texw = (float) texturedata.mtltexture.width;
  1285     const float texh = (float) texturedata.mtltexture.height;
  1120     const float texh = (float) texturedata.mtltexture.height;
  1286     float transform[16];
  1121     const float rads = (float)(M_PI * (float) angle / 180.0f);
       
  1122     const float c = cosf(rads), s = sinf(rads);
  1287     float minu, maxu, minv, maxv;
  1123     float minu, maxu, minv, maxv;
  1288 
  1124     // !!! FIXME: use an index buffer
  1289     METAL_SetupRenderCopy(data, texture, texturedata);
  1125     const size_t vertlen = (sizeof (float) * 32);
  1290 
  1126     float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, &cmd->data.draw.first);
  1291     minu = normtex(srcrect->x, texw);
  1127     if (!verts) {
  1292     maxu = normtex(srcrect->x + srcrect->w, texw);
  1128         return -1;
  1293     minv = normtex(srcrect->y, texh);
  1129     }
  1294     maxv = normtex(srcrect->y + srcrect->h, texh);
  1130 
       
  1131     cmd->data.draw.count = 1;
       
  1132 
       
  1133     minu = normtex(srcquad->x, texw);
       
  1134     maxu = normtex(srcquad->x + srcquad->w, texw);
       
  1135     minv = normtex(srcquad->y, texh);
       
  1136     maxv = normtex(srcquad->y + srcquad->h, texh);
  1295 
  1137 
  1296     if (flip & SDL_FLIP_HORIZONTAL) {
  1138     if (flip & SDL_FLIP_HORIZONTAL) {
  1297         float tmp = maxu;
  1139         float tmp = maxu;
  1298         maxu = minu;
  1140         maxu = minu;
  1299         minu = tmp;
  1141         minu = tmp;
  1302         float tmp = maxv;
  1144         float tmp = maxv;
  1303         maxv = minv;
  1145         maxv = minv;
  1304         minv = tmp;
  1146         minv = tmp;
  1305     }
  1147     }
  1306 
  1148 
  1307     const float uv[] = {
  1149     // vertices
  1308         minu, maxv,
  1150     *(verts++) = -center->x;
  1309         minu, minv,
  1151     *(verts++) = dstrect->h - center->y;
  1310         maxu, maxv,
  1152     *(verts++) = -center->x;
  1311         maxu, minv
  1153     *(verts++) = -center->y;
  1312     };
  1154     *(verts++) = dstrect->w - center->x;
  1313 
  1155     *(verts++) = dstrect->h - center->y;
  1314     const float xy[] = {
  1156     *(verts++) = dstrect->w - center->x;
  1315         -center->x, dstrect->h - center->y,
  1157     *(verts++) = -center->y;
  1316         -center->x, -center->y,
  1158 
  1317         dstrect->w - center->x, dstrect->h - center->y,
  1159     // texcoords
  1318         dstrect->w - center->x, -center->y
  1160     *(verts++) = minu;
  1319     };
  1161     *(verts++) = maxv;
  1320 
  1162     *(verts++) = minu;
  1321     {
  1163     *(verts++) = minv;
  1322         float rads = (float)(M_PI * (float) angle / 180.0f);
  1164     *(verts++) = maxu;
  1323         float c = cosf(rads), s = sinf(rads);
  1165     *(verts++) = maxv;
  1324         SDL_memset(transform, 0, sizeof(transform));
  1166     *(verts++) = maxu;
  1325 
  1167     *(verts++) = minv;
  1326         transform[10] = transform[15] = 1.0f;
  1168 
  1327 
  1169     // transform matrix
  1328         /* Rotation */
  1170     SDL_memset(verts, '\0', sizeof (*verts) * 16);
  1329         transform[0]  = c;
  1171     verts[10] = verts[15] = 1.0f;
  1330         transform[1]  = s;
  1172     // rotation
  1331         transform[4]  = -s;
  1173     verts[0] = c;
  1332         transform[5]  = c;
  1174     verts[1] = s;
  1333 
  1175     verts[4] = -s;
  1334         /* Translation */
  1176     verts[5] = c;
  1335         transform[12] = dstrect->x + center->x;
  1177 
  1336         transform[13] = dstrect->y + center->y;
  1178     // translation
  1337     }
  1179     verts[12] = dstrect->x + center->x;
  1338 
  1180     verts[13] = dstrect->y + center->y;
  1339     [data.mtlcmdencoder setVertexBytes:xy length:sizeof(xy) atIndex:0];
  1181 
  1340     [data.mtlcmdencoder setVertexBytes:uv length:sizeof(uv) atIndex:1];
  1182     return 0;
  1341     [data.mtlcmdencoder setVertexBytes:transform length:sizeof(transform) atIndex:3];
  1183 }
  1342     [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
  1184 
       
  1185 
       
  1186 typedef struct
       
  1187 {
       
  1188     id<MTLRenderPipelineState> pipeline;
       
  1189     size_t constants_offset;
       
  1190     SDL_Texture *texture;
       
  1191     Uint32 color;
       
  1192     SDL_bool color_dirty;
       
  1193     SDL_bool cliprect_dirty;
       
  1194     SDL_bool cliprect_enabled;
       
  1195     SDL_bool viewport_dirty;
       
  1196     SDL_Rect viewport;
       
  1197     SDL_Rect cliprect;
       
  1198 } METAL_DrawStateCache;
       
  1199 
       
  1200 static void
       
  1201 SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_MetalFragmentFunction shader,
       
  1202              const size_t constants_offset, id<MTLBuffer> mtlbufvertex, METAL_DrawStateCache *statecache)
       
  1203 {
       
  1204     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
       
  1205     const Uint8 r = cmd->data.draw.r;
       
  1206     const Uint8 g = cmd->data.draw.g;
       
  1207     const Uint8 b = cmd->data.draw.b;
       
  1208     const Uint8 a = cmd->data.draw.a;
       
  1209     const Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b);
       
  1210     const SDL_BlendMode blend = cmd->data.draw.blend;
       
  1211     id<MTLRenderPipelineState> newpipeline;
       
  1212 
       
  1213     METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad, NULL);
       
  1214 
       
  1215     if (statecache->viewport_dirty) {
       
  1216         MTLViewport viewport;
       
  1217         viewport.originX = statecache->viewport.x;
       
  1218         viewport.originY = statecache->viewport.y;
       
  1219         viewport.width = statecache->viewport.w;
       
  1220         viewport.height = statecache->viewport.h;
       
  1221         viewport.znear = 0.0;
       
  1222         viewport.zfar = 1.0;
       
  1223         [data.mtlcmdencoder setViewport:viewport];
       
  1224 
       
  1225         float projection[4][4];    /* Prepare an orthographic projection */
       
  1226         SDL_memset(projection, '\0', sizeof (projection));
       
  1227 
       
  1228         if (statecache->viewport.w && statecache->viewport.h) {
       
  1229             projection[0][0] = 2.0f / statecache->viewport.w;
       
  1230             projection[1][1] = -2.0f / statecache->viewport.h;
       
  1231             projection[3][0] = -1.0f;
       
  1232             projection[3][1] = 1.0f;
       
  1233             projection[3][3] = 1.0f;
       
  1234         }
       
  1235 
       
  1236         // !!! FIXME: This should be in a buffer...
       
  1237         [data.mtlcmdencoder setVertexBytes:projection length:sizeof(float)*16 atIndex:2];
       
  1238         statecache->viewport_dirty = SDL_FALSE;
       
  1239     }
       
  1240 
       
  1241     if (statecache->cliprect_dirty) {
       
  1242         MTLScissorRect mtlrect;
       
  1243         if (statecache->cliprect_enabled) {
       
  1244             const SDL_Rect *rect = &statecache->cliprect;
       
  1245             mtlrect.x = statecache->viewport.x + rect->x;
       
  1246             mtlrect.y = statecache->viewport.y + rect->y;
       
  1247             mtlrect.width = rect->w;
       
  1248             mtlrect.height = rect->h;
       
  1249         } else {
       
  1250             mtlrect.x = statecache->viewport.x;
       
  1251             mtlrect.y = statecache->viewport.y;
       
  1252             mtlrect.width = statecache->viewport.w;
       
  1253             mtlrect.height = statecache->viewport.h;
       
  1254         }
       
  1255         if (mtlrect.width > 0 && mtlrect.height > 0) {
       
  1256             [data.mtlcmdencoder setScissorRect:mtlrect];
       
  1257         }
       
  1258         statecache->cliprect_dirty = SDL_FALSE;
       
  1259     }
       
  1260 
       
  1261     if (statecache->color_dirty || (color != statecache->color)) {
       
  1262         const float colorf[4] = { ((float)r) / 255.0f, ((float)g) / 255.0f, ((float)b) / 255.0f, ((float)a) / 255.0f };
       
  1263         [data.mtlcmdencoder setFragmentBytes:colorf length:sizeof(colorf) atIndex:0];
       
  1264         statecache->color_dirty = SDL_FALSE;
       
  1265         statecache->color = color;
       
  1266     }
       
  1267 
       
  1268     newpipeline = ChoosePipelineState(data, data.activepipelines, shader, blend);
       
  1269     if (newpipeline != statecache->pipeline) {
       
  1270         [data.mtlcmdencoder setRenderPipelineState:newpipeline];
       
  1271         statecache->pipeline = newpipeline;
       
  1272     }
       
  1273 
       
  1274     if (constants_offset != statecache->constants_offset) {
       
  1275         if (constants_offset != CONSTANTS_OFFSET_INVALID) {
       
  1276             [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:constants_offset atIndex:3];
       
  1277         }
       
  1278         statecache->constants_offset = constants_offset;
       
  1279     }
       
  1280 
       
  1281     [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:cmd->data.draw.first atIndex:0];  // position
       
  1282 }
       
  1283 
       
  1284 static void
       
  1285 SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const size_t constants_offset,
       
  1286              id<MTLBuffer> mtlbufvertex, METAL_DrawStateCache *statecache)
       
  1287 {
       
  1288     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
       
  1289     SDL_Texture *texture = cmd->data.draw.texture;
       
  1290     METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
       
  1291 
       
  1292     SetDrawState(renderer, cmd, texturedata.fragmentFunction, constants_offset, mtlbufvertex, statecache);
       
  1293 
       
  1294     [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:cmd->data.draw.first+(8*sizeof (float)) atIndex:1];  // texcoords
       
  1295 
       
  1296     if (texture != statecache->texture) {
       
  1297         METAL_TextureData *oldtexturedata = NULL;
       
  1298         if (statecache->texture) {
       
  1299             oldtexturedata = (__bridge METAL_TextureData *) statecache->texture->driverdata;
       
  1300         }
       
  1301         if (!oldtexturedata || (texturedata.mtlsampler != oldtexturedata.mtlsampler)) {
       
  1302             [data.mtlcmdencoder setFragmentSamplerState:texturedata.mtlsampler atIndex:0];
       
  1303         }
       
  1304 
       
  1305         [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0];
       
  1306         if (texturedata.yuv || texturedata.nv12) {
       
  1307             [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture_uv atIndex:1];
       
  1308             [data.mtlcmdencoder setFragmentBuffer:data.mtlbufconstants offset:texturedata.conversionBufferOffset atIndex:1];
       
  1309         }
       
  1310         statecache->texture = texture;
       
  1311     }
       
  1312 }
       
  1313 
       
  1314 static int
       
  1315 METAL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
       
  1316 { @autoreleasepool {
       
  1317     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
       
  1318     METAL_DrawStateCache statecache;
       
  1319 
       
  1320     statecache.pipeline = nil;
       
  1321     statecache.constants_offset = CONSTANTS_OFFSET_INVALID;
       
  1322     statecache.texture = NULL;
       
  1323     statecache.color = ((renderer->a << 24) | (renderer->r << 16) | (renderer->g << 8) | renderer->b);
       
  1324     statecache.color_dirty = SDL_TRUE;
       
  1325     statecache.cliprect_dirty = SDL_TRUE;
       
  1326     statecache.viewport_dirty = SDL_TRUE;  // TRUE so we set ortho matrix
       
  1327     statecache.cliprect_enabled = renderer->clipping_enabled;
       
  1328     SDL_memcpy(&statecache.viewport, &renderer->viewport, sizeof (statecache.viewport));
       
  1329     SDL_memcpy(&statecache.cliprect, &renderer->clip_rect, sizeof (statecache.cliprect));
       
  1330 
       
  1331     // !!! FIXME: have a ring of pre-made MTLBuffers we cycle through? How expensive is creation?
       
  1332     id<MTLBuffer> mtlbufvertexstaging = [data.mtldevice newBufferWithLength:vertsize options:MTLResourceStorageModeShared];
       
  1333     #if !__has_feature(objc_arc)
       
  1334     [mtlbufvertexstaging autorelease];
       
  1335     #endif
       
  1336     mtlbufvertexstaging.label = @"SDL vertex staging data";
       
  1337     SDL_memcpy([mtlbufvertexstaging contents], vertices, vertsize);
       
  1338 
       
  1339     // Move our new vertex buffer from system RAM to GPU memory so any draw calls can use it.
       
  1340     id<MTLBuffer> mtlbufvertex = [data.mtldevice newBufferWithLength:vertsize options:MTLResourceStorageModePrivate];
       
  1341     #if !__has_feature(objc_arc)
       
  1342     [mtlbufvertex autorelease];
       
  1343     #endif
       
  1344     mtlbufvertex.label = @"SDL vertex data";
       
  1345     id<MTLCommandBuffer> cmdbuffer = [data.mtlcmdqueue commandBuffer];
       
  1346     id<MTLBlitCommandEncoder> blitcmd = [cmdbuffer blitCommandEncoder];
       
  1347     [blitcmd copyFromBuffer:mtlbufvertexstaging sourceOffset:0 toBuffer:mtlbufvertex destinationOffset:0 size:vertsize];
       
  1348     [blitcmd endEncoding];
       
  1349     [cmdbuffer commit];
       
  1350 
       
  1351     // If there's a command buffer here unexpectedly (app requested one?). Commit it so we can start fresh.
       
  1352     [data.mtlcmdencoder endEncoding];
       
  1353     [data.mtlcmdbuffer commit];
       
  1354     data.mtlcmdencoder = nil;
       
  1355     data.mtlcmdbuffer = nil;
       
  1356 
       
  1357     while (cmd) {
       
  1358         switch (cmd->command) {
       
  1359             case SDL_RENDERCMD_SETVIEWPORT: {
       
  1360                 if (SDL_memcmp(&statecache.viewport, &cmd->data.viewport, sizeof (statecache.viewport)) != 0) {
       
  1361                     SDL_memcpy(&statecache.viewport, &cmd->data.viewport, sizeof (statecache.viewport));
       
  1362                     statecache.viewport_dirty = SDL_TRUE;
       
  1363                 }
       
  1364                 break;
       
  1365             }
       
  1366 
       
  1367             case SDL_RENDERCMD_SETCLIPRECT: {
       
  1368                 if ((statecache.cliprect_enabled != cmd->data.cliprect.enabled) ||
       
  1369                     (SDL_memcmp(&statecache.cliprect, &cmd->data.cliprect.rect, sizeof (statecache.cliprect)) != 0)) {
       
  1370                     SDL_memcpy(&statecache.cliprect, &cmd->data.cliprect.rect, sizeof (statecache.cliprect));
       
  1371                     statecache.cliprect_enabled = cmd->data.cliprect.enabled;
       
  1372                     statecache.cliprect_dirty = SDL_TRUE;
       
  1373                 }
       
  1374                 break;
       
  1375             }
       
  1376 
       
  1377             case SDL_RENDERCMD_CLEAR: {
       
  1378                 /* If we're already encoding a command buffer, dump it without committing it. We'd just
       
  1379                     clear all its work anyhow, and starting a new encoder will let us use a hardware clear
       
  1380                     operation via MTLLoadActionClear. */
       
  1381                 if (data.mtlcmdencoder != nil) {
       
  1382                     [data.mtlcmdencoder endEncoding];
       
  1383                     data.mtlcmdencoder = nil;
       
  1384                     data.mtlcmdbuffer = nil;
       
  1385                 }
       
  1386 
       
  1387                 const Uint8 r = cmd->data.color.r;
       
  1388                 const Uint8 g = cmd->data.color.g;
       
  1389                 const Uint8 b = cmd->data.color.b;
       
  1390                 const Uint8 a = cmd->data.color.a;
       
  1391                 MTLClearColor color = MTLClearColorMake(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
       
  1392 
       
  1393                 // force all this state to be reconfigured on next command buffer.
       
  1394                 statecache.pipeline = nil;
       
  1395                 statecache.constants_offset = CONSTANTS_OFFSET_INVALID;
       
  1396                 statecache.texture = NULL;
       
  1397                 statecache.color_dirty = SDL_TRUE;
       
  1398                 statecache.cliprect_dirty = SDL_TRUE;
       
  1399                 statecache.viewport_dirty = SDL_TRUE;
       
  1400 
       
  1401                 // get new command encoder, set up with an initial clear operation.
       
  1402                 METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionClear, &color);
       
  1403                 break;
       
  1404             }
       
  1405 
       
  1406             case SDL_RENDERCMD_DRAW_POINTS:
       
  1407             case SDL_RENDERCMD_DRAW_LINES: {
       
  1408                 const size_t count = cmd->data.draw.count;
       
  1409                 const MTLPrimitiveType primtype = (cmd->command == SDL_RENDERCMD_DRAW_POINTS) ? MTLPrimitiveTypePoint : MTLPrimitiveTypeLineStrip;
       
  1410                 SetDrawState(renderer, cmd, SDL_METAL_FRAGMENT_SOLID, CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM, mtlbufvertex, &statecache);
       
  1411                 [data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];
       
  1412                 break;
       
  1413             }
       
  1414 
       
  1415             case SDL_RENDERCMD_FILL_RECTS: {
       
  1416                 const size_t count = cmd->data.draw.count;
       
  1417                 size_t start = 0;
       
  1418                 SetDrawState(renderer, cmd, SDL_METAL_FRAGMENT_SOLID, CONSTANTS_OFFSET_IDENTITY, mtlbufvertex, &statecache);
       
  1419                 for (size_t i = 0; i < count; i++, start += 4) {   // !!! FIXME: can we do all of these this with a single draw call, using MTLPrimitiveTypeTriangle and an index buffer?
       
  1420                     [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:start vertexCount:4];
       
  1421                 }
       
  1422                 break;
       
  1423             }
       
  1424 
       
  1425             case SDL_RENDERCMD_COPY: {
       
  1426                 SetCopyState(renderer, cmd, CONSTANTS_OFFSET_IDENTITY, mtlbufvertex, &statecache);
       
  1427                 [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
       
  1428                 break;
       
  1429             }
       
  1430 
       
  1431             case SDL_RENDERCMD_COPY_EX: {
       
  1432                 SetCopyState(renderer, cmd, CONSTANTS_OFFSET_INVALID, mtlbufvertex, &statecache);
       
  1433                 [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:cmd->data.draw.first+(16*sizeof (float)) atIndex:3];  // transform
       
  1434                 [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
       
  1435                 break;
       
  1436             }
       
  1437 
       
  1438             case SDL_RENDERCMD_NO_OP:
       
  1439                 break;
       
  1440         }
       
  1441         cmd = cmd->next;
       
  1442     }
  1343 
  1443 
  1344     return 0;
  1444     return 0;
  1345 }}
  1445 }}
  1346 
  1446 
  1347 static int
  1447 static int
  1348 METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
  1448 METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
  1349                     Uint32 pixel_format, void * pixels, int pitch)
  1449                     Uint32 pixel_format, void * pixels, int pitch)
  1350 { @autoreleasepool {
  1450 { @autoreleasepool {
  1351     METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
       
  1352 
       
  1353     // !!! FIXME: this probably needs to commit the current command buffer, and probably waitUntilCompleted
       
  1354     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
  1451     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
       
  1452     METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad, NULL);
       
  1453 
       
  1454     // Commit any current command buffer, and waitUntilCompleted, so any output is ready to be read.
       
  1455     [data.mtlcmdencoder endEncoding];
       
  1456     [data.mtlcmdbuffer commit];
       
  1457     [data.mtlcmdbuffer waitUntilCompleted];
       
  1458     data.mtlcmdencoder = nil;
       
  1459     data.mtlcmdbuffer = nil;
       
  1460 
  1355     id<MTLTexture> mtltexture = data.mtlpassdesc.colorAttachments[0].texture;
  1461     id<MTLTexture> mtltexture = data.mtlpassdesc.colorAttachments[0].texture;
  1356     MTLRegion mtlregion = MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h);
  1462     MTLRegion mtlregion = MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h);
  1357 
  1463 
  1358     // we only do BGRA8 or RGBA8 at the moment, so 4 will do.
  1464     // we only do BGRA8 or RGBA8 at the moment, so 4 will do.
  1359     const int temp_pitch = rect->w * 4;
  1465     const int temp_pitch = rect->w * 4;
  1420 }}
  1526 }}
  1421 
  1527 
  1422 static void *
  1528 static void *
  1423 METAL_GetMetalCommandEncoder(SDL_Renderer * renderer)
  1529 METAL_GetMetalCommandEncoder(SDL_Renderer * renderer)
  1424 { @autoreleasepool {
  1530 { @autoreleasepool {
  1425     METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad);
  1531     METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad, NULL);
  1426     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
  1532     METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
  1427     return (__bridge void*)data.mtlcmdencoder;
  1533     return (__bridge void*)data.mtlcmdencoder;
  1428 }}
  1534 }}
  1429 
  1535 
  1430 #endif /* SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED */
  1536 #endif /* SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED */