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