SDL  2.0
SDL_render_metal.m
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2020 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_metal.h"
30 #include "../SDL_sysrender.h"
31 
32 #include <Availability.h>
33 #import <Metal/Metal.h>
34 #import <QuartzCore/CAMetalLayer.h>
35 
36 #ifdef __MACOSX__
37 #import <AppKit/NSView.h>
38 #endif
39 
40 /* Regenerate these with build-metal-shaders.sh */
41 #ifdef __MACOSX__
42 #include "SDL_shaders_metal_osx.h"
43 #elif defined(__TVOS__)
44 #include "SDL_shaders_metal_tvos.h"
45 #else
46 #include "SDL_shaders_metal_ios.h"
47 #endif
48 
49 /* Apple Metal renderer implementation */
50 
51 /* macOS requires constants in a buffer to have a 256 byte alignment. */
52 /* Use native type alignments from https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf */
53 #ifdef __MACOSX__
54 #define CONSTANT_ALIGN(x) (256)
55 #else
56 #define CONSTANT_ALIGN(x) (x < 4 ? 4 : x)
57 #endif
58 
59 #define DEVICE_ALIGN(x) (x < 4 ? 4 : x)
60 
61 #define ALIGN_CONSTANTS(align, size) ((size + CONSTANT_ALIGN(align) - 1) & (~(CONSTANT_ALIGN(align) - 1)))
62 
63 static const size_t CONSTANTS_OFFSET_INVALID = 0xFFFFFFFF;
64 static const size_t CONSTANTS_OFFSET_IDENTITY = 0;
65 static const size_t CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM = ALIGN_CONSTANTS(16, CONSTANTS_OFFSET_IDENTITY + sizeof(float) * 16);
66 static const size_t CONSTANTS_OFFSET_DECODE_JPEG = ALIGN_CONSTANTS(16, CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM + sizeof(float) * 16);
67 static const size_t CONSTANTS_OFFSET_DECODE_BT601 = ALIGN_CONSTANTS(16, CONSTANTS_OFFSET_DECODE_JPEG + sizeof(float) * 4 * 4);
68 static const size_t CONSTANTS_OFFSET_DECODE_BT709 = ALIGN_CONSTANTS(16, CONSTANTS_OFFSET_DECODE_BT601 + sizeof(float) * 4 * 4);
69 static const size_t CONSTANTS_LENGTH = CONSTANTS_OFFSET_DECODE_BT709 + sizeof(float) * 4 * 4;
70 
71 typedef enum SDL_MetalVertexFunction
72 {
73  SDL_METAL_VERTEX_SOLID,
74  SDL_METAL_VERTEX_COPY,
75 } SDL_MetalVertexFunction;
76 
77 typedef enum SDL_MetalFragmentFunction
78 {
79  SDL_METAL_FRAGMENT_SOLID = 0,
80  SDL_METAL_FRAGMENT_COPY,
81  SDL_METAL_FRAGMENT_YUV,
82  SDL_METAL_FRAGMENT_NV12,
83  SDL_METAL_FRAGMENT_NV21,
84  SDL_METAL_FRAGMENT_COUNT,
85 } SDL_MetalFragmentFunction;
86 
87 typedef struct METAL_PipelineState
88 {
90  void *pipe;
91 } METAL_PipelineState;
92 
93 typedef struct METAL_PipelineCache
94 {
95  METAL_PipelineState *states;
96  int count;
97  SDL_MetalVertexFunction vertexFunction;
98  SDL_MetalFragmentFunction fragmentFunction;
99  MTLPixelFormat renderTargetFormat;
100  const char *label;
101 } METAL_PipelineCache;
102 
103 /* Each shader combination used by drawing functions has a separate pipeline
104  * cache, and we have a separate list of caches for each render target pixel
105  * format. This is more efficient than iterating over a global cache to find
106  * the pipeline based on the specified shader combination and RT pixel format,
107  * since we know what the RT pixel format is when we set the render target, and
108  * we know what the shader combination is inside each drawing function's code. */
109 typedef struct METAL_ShaderPipelines
110 {
111  MTLPixelFormat renderTargetFormat;
112  METAL_PipelineCache caches[SDL_METAL_FRAGMENT_COUNT];
113 } METAL_ShaderPipelines;
114 
115 @interface METAL_RenderData : NSObject
116  @property (nonatomic, retain) id<MTLDevice> mtldevice;
117  @property (nonatomic, retain) id<MTLCommandQueue> mtlcmdqueue;
118  @property (nonatomic, retain) id<MTLCommandBuffer> mtlcmdbuffer;
119  @property (nonatomic, retain) id<MTLRenderCommandEncoder> mtlcmdencoder;
120  @property (nonatomic, retain) id<MTLLibrary> mtllibrary;
121  @property (nonatomic, retain) id<CAMetalDrawable> mtlbackbuffer;
122  @property (nonatomic, retain) id<MTLSamplerState> mtlsamplernearest;
123  @property (nonatomic, retain) id<MTLSamplerState> mtlsamplerlinear;
124  @property (nonatomic, retain) id<MTLBuffer> mtlbufconstants;
125  @property (nonatomic, retain) id<MTLBuffer> mtlbufquadindices;
126  @property (nonatomic, assign) SDL_MetalView mtlview;
127  @property (nonatomic, retain) CAMetalLayer *mtllayer;
128  @property (nonatomic, retain) MTLRenderPassDescriptor *mtlpassdesc;
129  @property (nonatomic, assign) METAL_ShaderPipelines *activepipelines;
130  @property (nonatomic, assign) METAL_ShaderPipelines *allpipelines;
131  @property (nonatomic, assign) int pipelinescount;
132 @end
133 
134 @implementation METAL_RenderData
135 #if !__has_feature(objc_arc)
136 - (void)dealloc
137 {
138  [_mtldevice release];
139  [_mtlcmdqueue release];
140  [_mtlcmdbuffer release];
141  [_mtlcmdencoder release];
142  [_mtllibrary release];
143  [_mtlbackbuffer release];
144  [_mtlsamplernearest release];
145  [_mtlsamplerlinear release];
146  [_mtlbufconstants release];
147  [_mtlbufquadindices release];
148  [_mtllayer release];
149  [_mtlpassdesc release];
150  [super dealloc];
151 }
152 #endif
153 @end
154 
155 @interface METAL_TextureData : NSObject
156  @property (nonatomic, retain) id<MTLTexture> mtltexture;
157  @property (nonatomic, retain) id<MTLTexture> mtltexture_uv;
158  @property (nonatomic, retain) id<MTLSamplerState> mtlsampler;
159  @property (nonatomic, assign) SDL_MetalFragmentFunction fragmentFunction;
160  @property (nonatomic, assign) BOOL yuv;
161  @property (nonatomic, assign) BOOL nv12;
162  @property (nonatomic, assign) size_t conversionBufferOffset;
163  @property (nonatomic, assign) BOOL hasdata;
164 
165  @property (nonatomic, retain) id<MTLBuffer> lockedbuffer;
166  @property (nonatomic, assign) SDL_Rect lockedrect;
167 @end
168 
169 @implementation METAL_TextureData
170 #if !__has_feature(objc_arc)
171 - (void)dealloc
172 {
173  [_mtltexture release];
174  [_mtltexture_uv release];
175  [_mtlsampler release];
176  [_lockedbuffer release];
177  [super dealloc];
178 }
179 #endif
180 @end
181 
182 static int
183 IsMetalAvailable(const SDL_SysWMinfo *syswm)
184 {
185  if (syswm->subsystem != SDL_SYSWM_COCOA && syswm->subsystem != SDL_SYSWM_UIKIT) {
186  return SDL_SetError("Metal render target only supports Cocoa and UIKit video targets at the moment.");
187  }
188 
189  // this checks a weak symbol.
190 #if (defined(__MACOSX__) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101100))
191  if (MTLCreateSystemDefaultDevice == NULL) { // probably on 10.10 or lower.
192  return SDL_SetError("Metal framework not available on this system");
193  }
194 #endif
195 
196  return 0;
197 }
198 
199 static const MTLBlendOperation invalidBlendOperation = (MTLBlendOperation)0xFFFFFFFF;
200 static const MTLBlendFactor invalidBlendFactor = (MTLBlendFactor)0xFFFFFFFF;
201 
202 static MTLBlendOperation
203 GetBlendOperation(SDL_BlendOperation operation)
204 {
205  switch (operation) {
206  case SDL_BLENDOPERATION_ADD: return MTLBlendOperationAdd;
207  case SDL_BLENDOPERATION_SUBTRACT: return MTLBlendOperationSubtract;
208  case SDL_BLENDOPERATION_REV_SUBTRACT: return MTLBlendOperationReverseSubtract;
209  case SDL_BLENDOPERATION_MINIMUM: return MTLBlendOperationMin;
210  case SDL_BLENDOPERATION_MAXIMUM: return MTLBlendOperationMax;
211  default: return invalidBlendOperation;
212  }
213 }
214 
215 static MTLBlendFactor
216 GetBlendFactor(SDL_BlendFactor factor)
217 {
218  switch (factor) {
219  case SDL_BLENDFACTOR_ZERO: return MTLBlendFactorZero;
220  case SDL_BLENDFACTOR_ONE: return MTLBlendFactorOne;
221  case SDL_BLENDFACTOR_SRC_COLOR: return MTLBlendFactorSourceColor;
222  case SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return MTLBlendFactorOneMinusSourceColor;
223  case SDL_BLENDFACTOR_SRC_ALPHA: return MTLBlendFactorSourceAlpha;
224  case SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return MTLBlendFactorOneMinusSourceAlpha;
225  case SDL_BLENDFACTOR_DST_COLOR: return MTLBlendFactorDestinationColor;
226  case SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR: return MTLBlendFactorOneMinusDestinationColor;
227  case SDL_BLENDFACTOR_DST_ALPHA: return MTLBlendFactorDestinationAlpha;
228  case SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return MTLBlendFactorOneMinusDestinationAlpha;
229  default: return invalidBlendFactor;
230  }
231 }
232 
233 static NSString *
234 GetVertexFunctionName(SDL_MetalVertexFunction function)
235 {
236  switch (function) {
237  case SDL_METAL_VERTEX_SOLID: return @"SDL_Solid_vertex";
238  case SDL_METAL_VERTEX_COPY: return @"SDL_Copy_vertex";
239  default: return nil;
240  }
241 }
242 
243 static NSString *
244 GetFragmentFunctionName(SDL_MetalFragmentFunction function)
245 {
246  switch (function) {
247  case SDL_METAL_FRAGMENT_SOLID: return @"SDL_Solid_fragment";
248  case SDL_METAL_FRAGMENT_COPY: return @"SDL_Copy_fragment";
249  case SDL_METAL_FRAGMENT_YUV: return @"SDL_YUV_fragment";
250  case SDL_METAL_FRAGMENT_NV12: return @"SDL_NV12_fragment";
251  case SDL_METAL_FRAGMENT_NV21: return @"SDL_NV21_fragment";
252  default: return nil;
253  }
254 }
255 
257 MakePipelineState(METAL_RenderData *data, METAL_PipelineCache *cache,
258  NSString *blendlabel, SDL_BlendMode blendmode)
259 {
260  id<MTLFunction> mtlvertfn = [data.mtllibrary newFunctionWithName:GetVertexFunctionName(cache->vertexFunction)];
261  id<MTLFunction> mtlfragfn = [data.mtllibrary newFunctionWithName:GetFragmentFunctionName(cache->fragmentFunction)];
262  SDL_assert(mtlvertfn != nil);
263  SDL_assert(mtlfragfn != nil);
264 
265  MTLRenderPipelineDescriptor *mtlpipedesc = [[MTLRenderPipelineDescriptor alloc] init];
266  mtlpipedesc.vertexFunction = mtlvertfn;
267  mtlpipedesc.fragmentFunction = mtlfragfn;
268 
269  MTLVertexDescriptor *vertdesc = [MTLVertexDescriptor vertexDescriptor];
270 
271  switch (cache->vertexFunction) {
272  case SDL_METAL_VERTEX_SOLID:
273  /* position (float2) */
274  vertdesc.layouts[0].stride = sizeof(float) * 2;
275  vertdesc.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
276 
277  vertdesc.attributes[0].format = MTLVertexFormatFloat2;
278  vertdesc.attributes[0].offset = 0;
279  vertdesc.attributes[0].bufferIndex = 0;
280  break;
281  case SDL_METAL_VERTEX_COPY:
282  /* position (float2), texcoord (float2) */
283  vertdesc.layouts[0].stride = sizeof(float) * 4;
284  vertdesc.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
285 
286  vertdesc.attributes[0].format = MTLVertexFormatFloat2;
287  vertdesc.attributes[0].offset = 0;
288  vertdesc.attributes[0].bufferIndex = 0;
289 
290  vertdesc.attributes[1].format = MTLVertexFormatFloat2;
291  vertdesc.attributes[1].offset = sizeof(float) * 2;
292  vertdesc.attributes[1].bufferIndex = 0;
293  break;
294  }
295 
296  mtlpipedesc.vertexDescriptor = vertdesc;
297 
298  MTLRenderPipelineColorAttachmentDescriptor *rtdesc = mtlpipedesc.colorAttachments[0];
299  rtdesc.pixelFormat = cache->renderTargetFormat;
300 
301  if (blendmode != SDL_BLENDMODE_NONE) {
302  rtdesc.blendingEnabled = YES;
303  rtdesc.sourceRGBBlendFactor = GetBlendFactor(SDL_GetBlendModeSrcColorFactor(blendmode));
304  rtdesc.destinationRGBBlendFactor = GetBlendFactor(SDL_GetBlendModeDstColorFactor(blendmode));
305  rtdesc.rgbBlendOperation = GetBlendOperation(SDL_GetBlendModeColorOperation(blendmode));
306  rtdesc.sourceAlphaBlendFactor = GetBlendFactor(SDL_GetBlendModeSrcAlphaFactor(blendmode));
307  rtdesc.destinationAlphaBlendFactor = GetBlendFactor(SDL_GetBlendModeDstAlphaFactor(blendmode));
308  rtdesc.alphaBlendOperation = GetBlendOperation(SDL_GetBlendModeAlphaOperation(blendmode));
309  } else {
310  rtdesc.blendingEnabled = NO;
311  }
312 
313  mtlpipedesc.label = [@(cache->label) stringByAppendingString:blendlabel];
314 
315  NSError *err = nil;
316  id<MTLRenderPipelineState> state = [data.mtldevice newRenderPipelineStateWithDescriptor:mtlpipedesc error:&err];
317  SDL_assert(err == nil);
318 
319  METAL_PipelineState pipeline;
320  pipeline.blendMode = blendmode;
321  pipeline.pipe = (void *)CFBridgingRetain(state);
322 
323  METAL_PipelineState *states = SDL_realloc(cache->states, (cache->count + 1) * sizeof(pipeline));
324 
325 #if !__has_feature(objc_arc)
326  [mtlpipedesc release]; // !!! FIXME: can these be reused for each creation, or does the pipeline obtain it?
327  [mtlvertfn release];
328  [mtlfragfn release];
329  [state release];
330 #endif
331 
332  if (states) {
333  states[cache->count++] = pipeline;
334  cache->states = states;
335  return (__bridge id<MTLRenderPipelineState>)pipeline.pipe;
336  } else {
337  CFBridgingRelease(pipeline.pipe);
338  SDL_OutOfMemory();
339  return NULL;
340  }
341 }
342 
343 static void
344 MakePipelineCache(METAL_RenderData *data, METAL_PipelineCache *cache, const char *label,
345  MTLPixelFormat rtformat, SDL_MetalVertexFunction vertfn, SDL_MetalFragmentFunction fragfn)
346 {
347  SDL_zerop(cache);
348 
349  cache->vertexFunction = vertfn;
350  cache->fragmentFunction = fragfn;
351  cache->renderTargetFormat = rtformat;
352  cache->label = label;
353 
354  /* Create pipeline states for the default blend modes. Custom blend modes
355  * will be added to the cache on-demand. */
356  MakePipelineState(data, cache, @" (blend=none)", SDL_BLENDMODE_NONE);
357  MakePipelineState(data, cache, @" (blend=blend)", SDL_BLENDMODE_BLEND);
358  MakePipelineState(data, cache, @" (blend=add)", SDL_BLENDMODE_ADD);
359  MakePipelineState(data, cache, @" (blend=mod)", SDL_BLENDMODE_MOD);
360  MakePipelineState(data, cache, @" (blend=mul)", SDL_BLENDMODE_MUL);
361 }
362 
363 static void
364 DestroyPipelineCache(METAL_PipelineCache *cache)
365 {
366  if (cache != NULL) {
367  for (int i = 0; i < cache->count; i++) {
368  CFBridgingRelease(cache->states[i].pipe);
369  }
370 
371  SDL_free(cache->states);
372  }
373 }
374 
375 void
376 MakeShaderPipelines(METAL_RenderData *data, METAL_ShaderPipelines *pipelines, MTLPixelFormat rtformat)
377 {
379 
380  pipelines->renderTargetFormat = rtformat;
381 
382  MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_SOLID], "SDL primitives pipeline", rtformat, SDL_METAL_VERTEX_SOLID, SDL_METAL_FRAGMENT_SOLID);
383  MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_COPY], "SDL copy pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_COPY);
384  MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_YUV], "SDL YUV pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_YUV);
385  MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_NV12], "SDL NV12 pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_NV12);
386  MakePipelineCache(data, &pipelines->caches[SDL_METAL_FRAGMENT_NV21], "SDL NV21 pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_NV21);
387 }
388 
389 static METAL_ShaderPipelines *
390 ChooseShaderPipelines(METAL_RenderData *data, MTLPixelFormat rtformat)
391 {
392  METAL_ShaderPipelines *allpipelines = data.allpipelines;
393  int count = data.pipelinescount;
394 
395  for (int i = 0; i < count; i++) {
396  if (allpipelines[i].renderTargetFormat == rtformat) {
397  return &allpipelines[i];
398  }
399  }
400 
401  allpipelines = SDL_realloc(allpipelines, (count + 1) * sizeof(METAL_ShaderPipelines));
402 
403  if (allpipelines == NULL) {
404  SDL_OutOfMemory();
405  return NULL;
406  }
407 
408  MakeShaderPipelines(data, &allpipelines[count], rtformat);
409 
410  data.allpipelines = allpipelines;
411  data.pipelinescount = count + 1;
412 
413  return &data.allpipelines[count];
414 }
415 
416 static void
417 DestroyAllPipelines(METAL_ShaderPipelines *allpipelines, int count)
418 {
419  if (allpipelines != NULL) {
420  for (int i = 0; i < count; i++) {
421  for (int cache = 0; cache < SDL_METAL_FRAGMENT_COUNT; cache++) {
422  DestroyPipelineCache(&allpipelines[i].caches[cache]);
423  }
424  }
425 
426  SDL_free(allpipelines);
427  }
428 }
429 
430 static inline id<MTLRenderPipelineState>
431 ChoosePipelineState(METAL_RenderData *data, METAL_ShaderPipelines *pipelines, SDL_MetalFragmentFunction fragfn, SDL_BlendMode blendmode)
432 {
433  METAL_PipelineCache *cache = &pipelines->caches[fragfn];
434 
435  for (int i = 0; i < cache->count; i++) {
436  if (cache->states[i].blendMode == blendmode) {
437  return (__bridge id<MTLRenderPipelineState>)cache->states[i].pipe;
438  }
439  }
440 
441  return MakePipelineState(data, cache, [NSString stringWithFormat:@" (blend=custom 0x%x)", blendmode], blendmode);
442 }
443 
444 static void
445 METAL_ActivateRenderCommandEncoder(SDL_Renderer * renderer, MTLLoadAction load, MTLClearColor *clear_color, id<MTLBuffer> vertex_buffer)
446 {
447  METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
448 
449  /* Our SetRenderTarget just signals that the next render operation should
450  * set up a new render pass. This is where that work happens. */
451  if (data.mtlcmdencoder == nil) {
452  id<MTLTexture> mtltexture = nil;
453 
454  if (renderer->target != NULL) {
455  METAL_TextureData *texdata = (__bridge METAL_TextureData *)renderer->target->driverdata;
456  mtltexture = texdata.mtltexture;
457  } else {
458  if (data.mtlbackbuffer == nil) {
459  /* The backbuffer's contents aren't guaranteed to persist after
460  * presenting, so we can leave it undefined when loading it. */
461  data.mtlbackbuffer = [data.mtllayer nextDrawable];
462  if (load == MTLLoadActionLoad) {
463  load = MTLLoadActionDontCare;
464  }
465  }
466  mtltexture = data.mtlbackbuffer.texture;
467  }
468 
469  SDL_assert(mtltexture);
470 
471  if (load == MTLLoadActionClear) {
472  SDL_assert(clear_color != NULL);
473  data.mtlpassdesc.colorAttachments[0].clearColor = *clear_color;
474  }
475 
476  data.mtlpassdesc.colorAttachments[0].loadAction = load;
477  data.mtlpassdesc.colorAttachments[0].texture = mtltexture;
478 
479  data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
480  data.mtlcmdencoder = [data.mtlcmdbuffer renderCommandEncoderWithDescriptor:data.mtlpassdesc];
481 
482  if (data.mtlbackbuffer != nil && mtltexture == data.mtlbackbuffer.texture) {
483  data.mtlcmdencoder.label = @"SDL metal renderer backbuffer";
484  } else {
485  data.mtlcmdencoder.label = @"SDL metal renderer render target";
486  }
487 
488  /* Set up buffer bindings for positions, texcoords, and color once here,
489  * the offsets are adjusted in the code that uses them. */
490  if (vertex_buffer != nil) {
491  [data.mtlcmdencoder setVertexBuffer:vertex_buffer offset:0 atIndex:0];
492  [data.mtlcmdencoder setFragmentBuffer:vertex_buffer offset:0 atIndex:0];
493  }
494 
495  data.activepipelines = ChooseShaderPipelines(data, mtltexture.pixelFormat);
496 
497  // make sure this has a definite place in the queue. This way it will
498  // execute reliably whether the app tries to make its own command buffers
499  // or whatever. This means we can _always_ batch rendering commands!
500  [data.mtlcmdbuffer enqueue];
501  }
502 }
503 
504 static void
505 METAL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
506 {
507  if (event->event == SDL_WINDOWEVENT_SHOWN ||
508  event->event == SDL_WINDOWEVENT_HIDDEN) {
509  // !!! FIXME: write me
510  }
511 }
512 
513 static int
514 METAL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
515 { @autoreleasepool {
516  METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
517  if (w) {
518  *w = (int)data.mtllayer.drawableSize.width;
519  }
520  if (h) {
521  *h = (int)data.mtllayer.drawableSize.height;
522  }
523  return 0;
524 }}
525 
526 static SDL_bool
527 METAL_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode)
528 {
535 
536  if (GetBlendFactor(srcColorFactor) == invalidBlendFactor ||
537  GetBlendFactor(srcAlphaFactor) == invalidBlendFactor ||
538  GetBlendOperation(colorOperation) == invalidBlendOperation ||
539  GetBlendFactor(dstColorFactor) == invalidBlendFactor ||
540  GetBlendFactor(dstAlphaFactor) == invalidBlendFactor ||
541  GetBlendOperation(alphaOperation) == invalidBlendOperation) {
542  return SDL_FALSE;
543  }
544  return SDL_TRUE;
545 }
546 
547 static int
548 METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
549 { @autoreleasepool {
550  METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
551  MTLPixelFormat pixfmt;
552 
553  switch (texture->format) {
555  pixfmt = MTLPixelFormatRGBA8Unorm;
556  break;
558  pixfmt = MTLPixelFormatBGRA8Unorm;
559  break;
564  pixfmt = MTLPixelFormatR8Unorm;
565  break;
566  default:
567  return SDL_SetError("Texture format %s not supported by Metal", SDL_GetPixelFormatName(texture->format));
568  }
569 
570  MTLTextureDescriptor *mtltexdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:pixfmt
571  width:(NSUInteger)texture->w height:(NSUInteger)texture->h mipmapped:NO];
572 
573  /* Not available in iOS 8. */
574  if ([mtltexdesc respondsToSelector:@selector(usage)]) {
575  if (texture->access == SDL_TEXTUREACCESS_TARGET) {
576  mtltexdesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
577  } else {
578  mtltexdesc.usage = MTLTextureUsageShaderRead;
579  }
580  }
581 
582  id<MTLTexture> mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
583  if (mtltexture == nil) {
584  return SDL_SetError("Texture allocation failed");
585  }
586 
587  id<MTLTexture> mtltexture_uv = nil;
588 
589  BOOL yuv = (texture->format == SDL_PIXELFORMAT_IYUV) || (texture->format == SDL_PIXELFORMAT_YV12);
590  BOOL nv12 = (texture->format == SDL_PIXELFORMAT_NV12) || (texture->format == SDL_PIXELFORMAT_NV21);
591 
592  if (yuv) {
593  mtltexdesc.pixelFormat = MTLPixelFormatR8Unorm;
594  mtltexdesc.width = (texture->w + 1) / 2;
595  mtltexdesc.height = (texture->h + 1) / 2;
596  mtltexdesc.textureType = MTLTextureType2DArray;
597  mtltexdesc.arrayLength = 2;
598  } else if (nv12) {
599  mtltexdesc.pixelFormat = MTLPixelFormatRG8Unorm;
600  mtltexdesc.width = (texture->w + 1) / 2;
601  mtltexdesc.height = (texture->h + 1) / 2;
602  }
603 
604  if (yuv || nv12) {
605  mtltexture_uv = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
606  if (mtltexture_uv == nil) {
607 #if !__has_feature(objc_arc)
608  [mtltexture release];
609 #endif
610  return SDL_SetError("Texture allocation failed");
611  }
612  }
613 
614  METAL_TextureData *texturedata = [[METAL_TextureData alloc] init];
615  if (texture->scaleMode == SDL_ScaleModeNearest) {
616  texturedata.mtlsampler = data.mtlsamplernearest;
617  } else {
618  texturedata.mtlsampler = data.mtlsamplerlinear;
619  }
620  texturedata.mtltexture = mtltexture;
621  texturedata.mtltexture_uv = mtltexture_uv;
622 
623  texturedata.yuv = yuv;
624  texturedata.nv12 = nv12;
625 
626  if (yuv) {
627  texturedata.fragmentFunction = SDL_METAL_FRAGMENT_YUV;
628  } else if (texture->format == SDL_PIXELFORMAT_NV12) {
629  texturedata.fragmentFunction = SDL_METAL_FRAGMENT_NV12;
630  } else if (texture->format == SDL_PIXELFORMAT_NV21) {
631  texturedata.fragmentFunction = SDL_METAL_FRAGMENT_NV21;
632  } else {
633  texturedata.fragmentFunction = SDL_METAL_FRAGMENT_COPY;
634  }
635 
636  if (yuv || nv12) {
637  size_t offset = 0;
639  switch (mode) {
640  case SDL_YUV_CONVERSION_JPEG: offset = CONSTANTS_OFFSET_DECODE_JPEG; break;
641  case SDL_YUV_CONVERSION_BT601: offset = CONSTANTS_OFFSET_DECODE_BT601; break;
642  case SDL_YUV_CONVERSION_BT709: offset = CONSTANTS_OFFSET_DECODE_BT709; break;
643  default: offset = 0; break;
644  }
645  texturedata.conversionBufferOffset = offset;
646  }
647 
648  texture->driverdata = (void*)CFBridgingRetain(texturedata);
649 
650 #if !__has_feature(objc_arc)
651  [texturedata release];
652  [mtltexture release];
653  [mtltexture_uv release];
654 #endif
655 
656  return 0;
657 }}
658 
659 static void
660 METAL_UploadTextureData(id<MTLTexture> texture, SDL_Rect rect, int slice,
661  const void * pixels, int pitch)
662 {
663  [texture replaceRegion:MTLRegionMake2D(rect.x, rect.y, rect.w, rect.h)
664  mipmapLevel:0
665  slice:slice
666  withBytes:pixels
667  bytesPerRow:pitch
668  bytesPerImage:0];
669 }
670 
671 static MTLStorageMode
672 METAL_GetStorageMode(id<MTLResource> resource)
673 {
674  /* iOS 8 does not have this method. */
675  if ([resource respondsToSelector:@selector(storageMode)]) {
676  return resource.storageMode;
677  }
678  return MTLStorageModeShared;
679 }
680 
681 static int
682 METAL_UpdateTextureInternal(SDL_Renderer * renderer, METAL_TextureData *texturedata,
683  id<MTLTexture> texture, SDL_Rect rect, int slice,
684  const void * pixels, int pitch)
685 {
686  METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
687  SDL_Rect stagingrect = {0, 0, rect.w, rect.h};
688  MTLTextureDescriptor *desc;
689 
690  /* If the texture is managed or shared and this is the first upload, we can
691  * use replaceRegion to upload to it directly. Otherwise we upload the data
692  * to a staging texture and copy that over. */
693  if (!texturedata.hasdata && METAL_GetStorageMode(texture) != MTLStorageModePrivate) {
694  METAL_UploadTextureData(texture, rect, slice, pixels, pitch);
695  return 0;
696  }
697 
698  desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:texture.pixelFormat
699  width:rect.w
700  height:rect.h
701  mipmapped:NO];
702 
703  if (desc == nil) {
704  return SDL_OutOfMemory();
705  }
706 
707  /* TODO: We could have a pool of textures or a MTLHeap we allocate from,
708  * and release a staging texture back to the pool in the command buffer's
709  * completion handler. */
710  id<MTLTexture> stagingtex = [data.mtldevice newTextureWithDescriptor:desc];
711  if (stagingtex == nil) {
712  return SDL_OutOfMemory();
713  }
714 
715 #if !__has_feature(objc_arc)
716  [stagingtex autorelease];
717 #endif
718 
719  METAL_UploadTextureData(stagingtex, stagingrect, 0, pixels, pitch);
720 
721  if (data.mtlcmdencoder != nil) {
722  [data.mtlcmdencoder endEncoding];
723  data.mtlcmdencoder = nil;
724  }
725 
726  if (data.mtlcmdbuffer == nil) {
727  data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
728  }
729 
730  id<MTLBlitCommandEncoder> blitcmd = [data.mtlcmdbuffer blitCommandEncoder];
731 
732  [blitcmd copyFromTexture:stagingtex
733  sourceSlice:0
734  sourceLevel:0
735  sourceOrigin:MTLOriginMake(0, 0, 0)
736  sourceSize:MTLSizeMake(rect.w, rect.h, 1)
737  toTexture:texture
738  destinationSlice:slice
739  destinationLevel:0
740  destinationOrigin:MTLOriginMake(rect.x, rect.y, 0)];
741 
742  [blitcmd endEncoding];
743 
744  /* TODO: This isn't very efficient for the YUV formats, which call
745  * UpdateTextureInternal multiple times in a row. */
746  [data.mtlcmdbuffer commit];
747  data.mtlcmdbuffer = nil;
748 
749  return 0;
750 }
751 
752 static int
753 METAL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
754  const SDL_Rect * rect, const void *pixels, int pitch)
755 { @autoreleasepool {
756  METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
757 
758  if (METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltexture, *rect, 0, pixels, pitch) < 0) {
759  return -1;
760  }
761 
762  if (texturedata.yuv) {
763  int Uslice = texture->format == SDL_PIXELFORMAT_YV12 ? 1 : 0;
764  int Vslice = texture->format == SDL_PIXELFORMAT_YV12 ? 0 : 1;
765  int UVpitch = (pitch + 1) / 2;
766  SDL_Rect UVrect = {rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2};
767 
768  /* Skip to the correct offset into the next texture */
769  pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
770  if (METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltexture_uv, UVrect, Uslice, pixels, UVpitch) < 0) {
771  return -1;
772  }
773 
774  /* Skip to the correct offset into the next texture */
775  pixels = (const void*)((const Uint8*)pixels + UVrect.h * UVpitch);
776  if (METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltexture_uv, UVrect, Vslice, pixels, UVpitch) < 0) {
777  return -1;
778  }
779  }
780 
781  if (texturedata.nv12) {
782  SDL_Rect UVrect = {rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2};
783  int UVpitch = 2 * ((pitch + 1) / 2);
784 
785  /* Skip to the correct offset into the next texture */
786  pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);
787  if (METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltexture_uv, UVrect, 0, pixels, UVpitch) < 0) {
788  return -1;
789  }
790  }
791 
792  texturedata.hasdata = YES;
793 
794  return 0;
795 }}
796 
797 static int
798 METAL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
799  const SDL_Rect * rect,
800  const Uint8 *Yplane, int Ypitch,
801  const Uint8 *Uplane, int Upitch,
802  const Uint8 *Vplane, int Vpitch)
803 { @autoreleasepool {
804  METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
805  const int Uslice = 0;
806  const int Vslice = 1;
807  SDL_Rect UVrect = {rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2};
808 
809  /* Bail out if we're supposed to update an empty rectangle */
810  if (rect->w <= 0 || rect->h <= 0) {
811  return 0;
812  }
813 
814  if (METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltexture, *rect, 0, Yplane, Ypitch) < 0) {
815  return -1;
816  }
817  if (METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltexture_uv, UVrect, Uslice, Uplane, Upitch)) {
818  return -1;
819  }
820  if (METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltexture_uv, UVrect, Vslice, Vplane, Vpitch)) {
821  return -1;
822  }
823 
824  texturedata.hasdata = YES;
825 
826  return 0;
827 }}
828 
829 static int
830 METAL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
831  const SDL_Rect * rect, void **pixels, int *pitch)
832 { @autoreleasepool {
833  METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
834  METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
835  int buffersize = 0;
836  id<MTLBuffer> lockedbuffer = nil;
837 
838  if (rect->w <= 0 || rect->h <= 0) {
839  return SDL_SetError("Invalid rectangle dimensions for LockTexture.");
840  }
841 
842  *pitch = SDL_BYTESPERPIXEL(texture->format) * rect->w;
843 
844  if (texturedata.yuv || texturedata.nv12) {
845  buffersize = ((*pitch) * rect->h) + (2 * (*pitch + 1) / 2) * ((rect->h + 1) / 2);
846  } else {
847  buffersize = (*pitch) * rect->h;
848  }
849 
850  lockedbuffer = [data.mtldevice newBufferWithLength:buffersize options:MTLResourceStorageModeShared];
851  if (lockedbuffer == nil) {
852  return SDL_OutOfMemory();
853  }
854 
855  texturedata.lockedrect = *rect;
856  texturedata.lockedbuffer = lockedbuffer;
857  *pixels = [lockedbuffer contents];
858 
859  /* METAL_TextureData.lockedbuffer retains. */
860 #if !__has_feature(objc_arc)
861  [lockedbuffer release];
862 #endif
863 
864  return 0;
865 }}
866 
867 static void
868 METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
869 { @autoreleasepool {
870  METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
871  METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
872  SDL_Rect rect = texturedata.lockedrect;
873  int pitch = SDL_BYTESPERPIXEL(texture->format) * rect.w;
874  SDL_Rect UVrect = {rect.x / 2, rect.y / 2, (rect.w + 1) / 2, (rect.h + 1) / 2};
875 
876  if (texturedata.lockedbuffer == nil) {
877  return;
878  }
879 
880  if (data.mtlcmdencoder != nil) {
881  [data.mtlcmdencoder endEncoding];
882  data.mtlcmdencoder = nil;
883  }
884 
885  if (data.mtlcmdbuffer == nil) {
886  data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
887  }
888 
889  id<MTLBlitCommandEncoder> blitcmd = [data.mtlcmdbuffer blitCommandEncoder];
890 
891  [blitcmd copyFromBuffer:texturedata.lockedbuffer
892  sourceOffset:0
893  sourceBytesPerRow:pitch
894  sourceBytesPerImage:0
895  sourceSize:MTLSizeMake(rect.w, rect.h, 1)
896  toTexture:texturedata.mtltexture
897  destinationSlice:0
898  destinationLevel:0
899  destinationOrigin:MTLOriginMake(rect.x, rect.y, 0)];
900 
901  if (texturedata.yuv) {
902  int Uslice = texture->format == SDL_PIXELFORMAT_YV12 ? 1 : 0;
903  int Vslice = texture->format == SDL_PIXELFORMAT_YV12 ? 0 : 1;
904  int UVpitch = (pitch + 1) / 2;
905 
906  [blitcmd copyFromBuffer:texturedata.lockedbuffer
907  sourceOffset:rect.h * pitch
908  sourceBytesPerRow:UVpitch
909  sourceBytesPerImage:UVpitch * UVrect.h
910  sourceSize:MTLSizeMake(UVrect.w, UVrect.h, 1)
911  toTexture:texturedata.mtltexture_uv
912  destinationSlice:Uslice
913  destinationLevel:0
914  destinationOrigin:MTLOriginMake(UVrect.x, UVrect.y, 0)];
915 
916  [blitcmd copyFromBuffer:texturedata.lockedbuffer
917  sourceOffset:(rect.h * pitch) + UVrect.h * UVpitch
918  sourceBytesPerRow:UVpitch
919  sourceBytesPerImage:UVpitch * UVrect.h
920  sourceSize:MTLSizeMake(UVrect.w, UVrect.h, 1)
921  toTexture:texturedata.mtltexture_uv
922  destinationSlice:Vslice
923  destinationLevel:0
924  destinationOrigin:MTLOriginMake(UVrect.x, UVrect.y, 0)];
925  }
926 
927  if (texturedata.nv12) {
928  int UVpitch = 2 * ((pitch + 1) / 2);
929 
930  [blitcmd copyFromBuffer:texturedata.lockedbuffer
931  sourceOffset:rect.h * pitch
932  sourceBytesPerRow:UVpitch
933  sourceBytesPerImage:0
934  sourceSize:MTLSizeMake(UVrect.w, UVrect.h, 1)
935  toTexture:texturedata.mtltexture_uv
936  destinationSlice:0
937  destinationLevel:0
938  destinationOrigin:MTLOriginMake(UVrect.x, UVrect.y, 0)];
939  }
940 
941  [blitcmd endEncoding];
942 
943  [data.mtlcmdbuffer commit];
944  data.mtlcmdbuffer = nil;
945 
946  texturedata.lockedbuffer = nil; /* Retained property, so it calls release. */
947  texturedata.hasdata = YES;
948 }}
949 
950 static void
951 METAL_SetTextureScaleMode(SDL_Renderer * renderer, SDL_Texture * texture, SDL_ScaleMode scaleMode)
952 { @autoreleasepool {
953  METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
954  METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
955 
956  if (scaleMode == SDL_ScaleModeNearest) {
957  texturedata.mtlsampler = data.mtlsamplernearest;
958  } else {
959  texturedata.mtlsampler = data.mtlsamplerlinear;
960  }
961 }}
962 
963 static int
964 METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
965 { @autoreleasepool {
966  METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
967 
968  if (data.mtlcmdencoder) {
969  /* End encoding for the previous render target so we can set up a new
970  * render pass for this one. */
971  [data.mtlcmdencoder endEncoding];
972  [data.mtlcmdbuffer commit];
973 
974  data.mtlcmdencoder = nil;
975  data.mtlcmdbuffer = nil;
976  }
977 
978  /* We don't begin a new render pass right away - we delay it until an actual
979  * draw or clear happens. That way we can use hardware clears when possible,
980  * which are only available when beginning a new render pass. */
981  return 0;
982 }}
983 
984 
985 // normalize a value from 0.0f to len into 0.0f to 1.0f.
986 static inline float
987 normtex(const float _val, const float len)
988 {
989  return _val / len;
990 }
991 
992 static int
993 METAL_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd)
994 {
995  float projection[4][4]; /* Prepare an orthographic projection */
996  const int w = cmd->data.viewport.rect.w;
997  const int h = cmd->data.viewport.rect.h;
998  const size_t matrixlen = sizeof (projection);
999  float *matrix = (float *) SDL_AllocateRenderVertices(renderer, matrixlen, CONSTANT_ALIGN(16), &cmd->data.viewport.first);
1000  if (!matrix) {
1001  return -1;
1002  }
1003 
1004  SDL_memset(projection, '\0', matrixlen);
1005  if (w && h) {
1006  projection[0][0] = 2.0f / w;
1007  projection[1][1] = -2.0f / h;
1008  projection[3][0] = -1.0f;
1009  projection[3][1] = 1.0f;
1010  projection[3][3] = 1.0f;
1011  }
1012  SDL_memcpy(matrix, projection, matrixlen);
1013 
1014  return 0;
1015 }
1016 
1017 static int
1018 METAL_QueueSetDrawColor(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
1019 {
1020  const size_t vertlen = sizeof (float) * 4;
1021  float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, DEVICE_ALIGN(16), &cmd->data.color.first);
1022  if (!verts) {
1023  return -1;
1024  }
1025  *(verts++) = ((float)cmd->data.color.r) / 255.0f;
1026  *(verts++) = ((float)cmd->data.color.g) / 255.0f;
1027  *(verts++) = ((float)cmd->data.color.b) / 255.0f;
1028  *(verts++) = ((float)cmd->data.color.a) / 255.0f;
1029  return 0;
1030 }
1031 
1032 static int
1033 METAL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count)
1034 {
1035  const size_t vertlen = (sizeof (float) * 2) * count;
1036  float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, DEVICE_ALIGN(8), &cmd->data.draw.first);
1037  if (!verts) {
1038  return -1;
1039  }
1040  cmd->data.draw.count = count;
1041  SDL_memcpy(verts, points, vertlen);
1042  return 0;
1043 }
1044 
1045 static int
1046 METAL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count)
1047 {
1048  const size_t vertlen = (sizeof (float) * 8) * count;
1049  float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, DEVICE_ALIGN(8), &cmd->data.draw.first);
1050  if (!verts) {
1051  return -1;
1052  }
1053 
1054  cmd->data.draw.count = count;
1055 
1056  /* Quads in the following vertex order (matches the quad index buffer):
1057  * 1---3
1058  * | \ |
1059  * 0---2
1060  */
1061  for (int i = 0; i < count; i++, rects++) {
1062  if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) {
1063  cmd->data.draw.count--;
1064  } else {
1065  *(verts++) = rects->x;
1066  *(verts++) = rects->y + rects->h;
1067  *(verts++) = rects->x;
1068  *(verts++) = rects->y;
1069  *(verts++) = rects->x + rects->w;
1070  *(verts++) = rects->y + rects->h;
1071  *(verts++) = rects->x + rects->w;
1072  *(verts++) = rects->y;
1073  }
1074  }
1075 
1076  if (cmd->data.draw.count == 0) {
1077  cmd->command = SDL_RENDERCMD_NO_OP; // nothing to do, just skip this one later.
1078  }
1079 
1080  return 0;
1081 }
1082 
1083 static int
1084 METAL_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
1085  const SDL_Rect * srcrect, const SDL_FRect * dstrect)
1086 {
1087  const float texw = (float) texture->w;
1088  const float texh = (float) texture->h;
1089  // !!! FIXME: use an index buffer
1090  const size_t vertlen = (sizeof (float) * 16);
1091  float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, DEVICE_ALIGN(8), &cmd->data.draw.first);
1092  if (!verts) {
1093  return -1;
1094  }
1095 
1096  cmd->data.draw.count = 1;
1097 
1098  /* Interleaved positions and texture coordinates */
1099  *(verts++) = dstrect->x;
1100  *(verts++) = dstrect->y + dstrect->h;
1101  *(verts++) = normtex(srcrect->x, texw);
1102  *(verts++) = normtex(srcrect->y + srcrect->h, texh);
1103 
1104  *(verts++) = dstrect->x;
1105  *(verts++) = dstrect->y;
1106  *(verts++) = normtex(srcrect->x, texw);
1107  *(verts++) = normtex(srcrect->y, texh);
1108 
1109  *(verts++) = dstrect->x + dstrect->w;
1110  *(verts++) = dstrect->y + dstrect->h;
1111  *(verts++) = normtex(srcrect->x + srcrect->w, texw);
1112  *(verts++) = normtex(srcrect->y + srcrect->h, texh);
1113 
1114  *(verts++) = dstrect->x + dstrect->w;
1115  *(verts++) = dstrect->y;
1116  *(verts++) = normtex(srcrect->x + srcrect->w, texw);
1117  *(verts++) = normtex(srcrect->y, texh);
1118 
1119  return 0;
1120 }
1121 
1122 static int
1123 METAL_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
1124  const SDL_Rect * srcquad, const SDL_FRect * dstrect,
1125  const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
1126 {
1127  const float texw = (float) texture->w;
1128  const float texh = (float) texture->h;
1129  const float rads = (float)(M_PI * (float) angle / 180.0f);
1130  const float c = cosf(rads), s = sinf(rads);
1131  float minu, maxu, minv, maxv;
1132  const size_t vertlen = (sizeof (float) * 32);
1133  float *verts;
1134 
1135  // cheat and store this offset in (count) because it needs to be aligned in ways other fields don't and we aren't using count otherwise.
1136  verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, CONSTANT_ALIGN(16), &cmd->data.draw.count);
1137  if (!verts) {
1138  return -1;
1139  }
1140 
1141  // transform matrix
1142  SDL_memset(verts, '\0', sizeof (*verts) * 16);
1143  verts[10] = verts[15] = 1.0f;
1144  // rotation
1145  verts[0] = c;
1146  verts[1] = s;
1147  verts[4] = -s;
1148  verts[5] = c;
1149 
1150  // translation
1151  verts[12] = dstrect->x + center->x;
1152  verts[13] = dstrect->y + center->y;
1153 
1154  // rest of the vertices don't need the aggressive alignment. Pack them in.
1155  verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, DEVICE_ALIGN(8), &cmd->data.draw.first);
1156  if (!verts) {
1157  return -1;
1158  }
1159 
1160  minu = normtex(srcquad->x, texw);
1161  maxu = normtex(srcquad->x + srcquad->w, texw);
1162  minv = normtex(srcquad->y, texh);
1163  maxv = normtex(srcquad->y + srcquad->h, texh);
1164 
1165  if (flip & SDL_FLIP_HORIZONTAL) {
1166  float tmp = maxu;
1167  maxu = minu;
1168  minu = tmp;
1169  }
1170  if (flip & SDL_FLIP_VERTICAL) {
1171  float tmp = maxv;
1172  maxv = minv;
1173  minv = tmp;
1174  }
1175 
1176  /* Interleaved positions and texture coordinates */
1177  *(verts++) = -center->x;
1178  *(verts++) = dstrect->h - center->y;
1179  *(verts++) = minu;
1180  *(verts++) = maxv;
1181 
1182  *(verts++) = -center->x;
1183  *(verts++) = -center->y;
1184  *(verts++) = minu;
1185  *(verts++) = minv;
1186 
1187  *(verts++) = dstrect->w - center->x;
1188  *(verts++) = dstrect->h - center->y;
1189  *(verts++) = maxu;
1190  *(verts++) = maxv;
1191 
1192  *(verts++) = dstrect->w - center->x;
1193  *(verts++) = -center->y;
1194  *(verts++) = maxu;
1195  *(verts++) = minv;
1196 
1197  return 0;
1198 }
1199 
1200 
1201 typedef struct
1202 {
1203  #if __has_feature(objc_arc)
1204  __unsafe_unretained id<MTLRenderPipelineState> pipeline;
1205  __unsafe_unretained id<MTLBuffer> vertex_buffer;
1206  #else
1207  id<MTLRenderPipelineState> pipeline;
1208  id<MTLBuffer> vertex_buffer;
1209  #endif
1210  size_t constants_offset;
1212  SDL_bool cliprect_dirty;
1213  SDL_bool cliprect_enabled;
1214  SDL_Rect cliprect;
1215  SDL_bool viewport_dirty;
1217  size_t projection_offset;
1218  SDL_bool color_dirty;
1219  size_t color_offset;
1220 } METAL_DrawStateCache;
1221 
1222 static void
1223 SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_MetalFragmentFunction shader,
1224  const size_t constants_offset, id<MTLBuffer> mtlbufvertex, METAL_DrawStateCache *statecache)
1225 {
1226  METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
1227  const SDL_BlendMode blend = cmd->data.draw.blend;
1228  size_t first = cmd->data.draw.first;
1229  id<MTLRenderPipelineState> newpipeline;
1230 
1231  METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad, NULL, statecache->vertex_buffer);
1232 
1233  if (statecache->viewport_dirty) {
1234  MTLViewport viewport;
1235  viewport.originX = statecache->viewport.x;
1236  viewport.originY = statecache->viewport.y;
1237  viewport.width = statecache->viewport.w;
1238  viewport.height = statecache->viewport.h;
1239  viewport.znear = 0.0;
1240  viewport.zfar = 1.0;
1241  [data.mtlcmdencoder setViewport:viewport];
1242  [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:statecache->projection_offset atIndex:2]; // projection
1243  statecache->viewport_dirty = SDL_FALSE;
1244  }
1245 
1246  if (statecache->cliprect_dirty) {
1247  MTLScissorRect mtlrect;
1248  if (statecache->cliprect_enabled) {
1249  const SDL_Rect *rect = &statecache->cliprect;
1250  mtlrect.x = statecache->viewport.x + rect->x;
1251  mtlrect.y = statecache->viewport.y + rect->y;
1252  mtlrect.width = rect->w;
1253  mtlrect.height = rect->h;
1254  } else {
1255  mtlrect.x = statecache->viewport.x;
1256  mtlrect.y = statecache->viewport.y;
1257  mtlrect.width = statecache->viewport.w;
1258  mtlrect.height = statecache->viewport.h;
1259  }
1260  if (mtlrect.width > 0 && mtlrect.height > 0) {
1261  [data.mtlcmdencoder setScissorRect:mtlrect];
1262  }
1263  statecache->cliprect_dirty = SDL_FALSE;
1264  }
1265 
1266  if (statecache->color_dirty) {
1267  [data.mtlcmdencoder setFragmentBufferOffset:statecache->color_offset atIndex:0];
1268  statecache->color_dirty = SDL_FALSE;
1269  }
1270 
1271  newpipeline = ChoosePipelineState(data, data.activepipelines, shader, blend);
1272  if (newpipeline != statecache->pipeline) {
1273  [data.mtlcmdencoder setRenderPipelineState:newpipeline];
1274  statecache->pipeline = newpipeline;
1275  }
1276 
1277  if (constants_offset != statecache->constants_offset) {
1278  if (constants_offset != CONSTANTS_OFFSET_INVALID) {
1279  [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:constants_offset atIndex:3];
1280  }
1281  statecache->constants_offset = constants_offset;
1282  }
1283 
1284  [data.mtlcmdencoder setVertexBufferOffset:first atIndex:0]; /* position/texcoords */
1285 }
1286 
1287 static void
1288 SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const size_t constants_offset,
1289  id<MTLBuffer> mtlbufvertex, METAL_DrawStateCache *statecache)
1290 {
1291  METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
1292  SDL_Texture *texture = cmd->data.draw.texture;
1293  METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
1294 
1295  SetDrawState(renderer, cmd, texturedata.fragmentFunction, constants_offset, mtlbufvertex, statecache);
1296 
1297  if (texture != statecache->texture) {
1298  METAL_TextureData *oldtexturedata = NULL;
1299  if (statecache->texture) {
1300  oldtexturedata = (__bridge METAL_TextureData *) statecache->texture->driverdata;
1301  }
1302  if (!oldtexturedata || (texturedata.mtlsampler != oldtexturedata.mtlsampler)) {
1303  [data.mtlcmdencoder setFragmentSamplerState:texturedata.mtlsampler atIndex:0];
1304  }
1305 
1306  [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0];
1307  if (texturedata.yuv || texturedata.nv12) {
1308  [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture_uv atIndex:1];
1309  [data.mtlcmdencoder setFragmentBuffer:data.mtlbufconstants offset:texturedata.conversionBufferOffset atIndex:1];
1310  }
1311  statecache->texture = texture;
1312  }
1313 }
1314 
1315 static int
1316 METAL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
1317 { @autoreleasepool {
1318  METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
1319  METAL_DrawStateCache statecache;
1320  SDL_zero(statecache);
1321 
1322  id<MTLBuffer> mtlbufvertex = nil;
1323 
1324  statecache.pipeline = nil;
1325  statecache.vertex_buffer = nil;
1326  statecache.constants_offset = CONSTANTS_OFFSET_INVALID;
1327  statecache.texture = NULL;
1328  statecache.color_dirty = SDL_TRUE;
1329  statecache.cliprect_dirty = SDL_TRUE;
1330  statecache.viewport_dirty = SDL_TRUE;
1331  statecache.projection_offset = 0;
1332  statecache.color_offset = 0;
1333 
1334  // !!! FIXME: have a ring of pre-made MTLBuffers we cycle through? How expensive is creation?
1335  if (vertsize > 0) {
1336  /* We can memcpy to a shared buffer from the CPU and read it from the GPU
1337  * without any extra copying. It's a bit slower on macOS to read shared
1338  * data from the GPU than to read managed/private data, but we avoid the
1339  * cost of copying the data and the code's simpler. Apple's best
1340  * practices guide recommends this approach for streamed vertex data.
1341  * TODO: this buffer is also used for constants. Is performance still
1342  * good for those, or should we have a managed buffer for them? */
1343  mtlbufvertex = [data.mtldevice newBufferWithLength:vertsize options:MTLResourceStorageModeShared];
1344  #if !__has_feature(objc_arc)
1345  [mtlbufvertex autorelease];
1346  #endif
1347  mtlbufvertex.label = @"SDL vertex data";
1348  SDL_memcpy([mtlbufvertex contents], vertices, vertsize);
1349 
1350  statecache.vertex_buffer = mtlbufvertex;
1351  }
1352 
1353  // If there's a command buffer here unexpectedly (app requested one?). Commit it so we can start fresh.
1354  [data.mtlcmdencoder endEncoding];
1355  [data.mtlcmdbuffer commit];
1356  data.mtlcmdencoder = nil;
1357  data.mtlcmdbuffer = nil;
1358 
1359  while (cmd) {
1360  switch (cmd->command) {
1362  SDL_memcpy(&statecache.viewport, &cmd->data.viewport.rect, sizeof (statecache.viewport));
1363  statecache.projection_offset = cmd->data.viewport.first;
1364  statecache.viewport_dirty = SDL_TRUE;
1365  statecache.cliprect_dirty = SDL_TRUE;
1366  break;
1367  }
1368 
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  break;
1374  }
1375 
1377  statecache.color_offset = cmd->data.color.first;
1378  statecache.color_dirty = SDL_TRUE;
1379  break;
1380  }
1381 
1382  case SDL_RENDERCMD_CLEAR: {
1383  /* If we're already encoding a command buffer, dump it without committing it. We'd just
1384  clear all its work anyhow, and starting a new encoder will let us use a hardware clear
1385  operation via MTLLoadActionClear. */
1386  if (data.mtlcmdencoder != nil) {
1387  [data.mtlcmdencoder endEncoding];
1388 
1389  // !!! FIXME: have to commit, or an uncommitted but enqueued buffer will prevent the frame from finishing.
1390  [data.mtlcmdbuffer commit];
1391  data.mtlcmdencoder = nil;
1392  data.mtlcmdbuffer = nil;
1393  }
1394 
1395  // force all this state to be reconfigured on next command buffer.
1396  statecache.pipeline = nil;
1397  statecache.constants_offset = CONSTANTS_OFFSET_INVALID;
1398  statecache.texture = NULL;
1399  statecache.color_dirty = SDL_TRUE;
1400  statecache.cliprect_dirty = SDL_TRUE;
1401  statecache.viewport_dirty = SDL_TRUE;
1402 
1403  const Uint8 r = cmd->data.color.r;
1404  const Uint8 g = cmd->data.color.g;
1405  const Uint8 b = cmd->data.color.b;
1406  const Uint8 a = cmd->data.color.a;
1407  MTLClearColor color = MTLClearColorMake(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
1408 
1409  // get new command encoder, set up with an initial clear operation.
1410  METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionClear, &color, mtlbufvertex);
1411  break;
1412  }
1413 
1415  case SDL_RENDERCMD_DRAW_LINES: {
1416  const size_t count = cmd->data.draw.count;
1417  const MTLPrimitiveType primtype = (cmd->command == SDL_RENDERCMD_DRAW_POINTS) ? MTLPrimitiveTypePoint : MTLPrimitiveTypeLineStrip;
1418  SetDrawState(renderer, cmd, SDL_METAL_FRAGMENT_SOLID, CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM, mtlbufvertex, &statecache);
1419  [data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];
1420  break;
1421  }
1422 
1423  case SDL_RENDERCMD_FILL_RECTS: {
1424  const size_t count = cmd->data.draw.count;
1425  const size_t maxcount = UINT16_MAX / 4;
1426  SetDrawState(renderer, cmd, SDL_METAL_FRAGMENT_SOLID, CONSTANTS_OFFSET_IDENTITY, mtlbufvertex, &statecache);
1427  if (count == 1) {
1428  [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
1429  } else {
1430  /* Our index buffer has 16 bit indices, so we can only draw
1431  * 65k vertices (16k rects) at a time. */
1432  for (size_t i = 0; i < count; i += maxcount) {
1433  /* Set the vertex buffer offset for our current positions.
1434  * The vertex buffer itself was bound in SetDrawState. */
1435  [data.mtlcmdencoder setVertexBufferOffset:cmd->data.draw.first + i*sizeof(float)*8 atIndex:0];
1436  [data.mtlcmdencoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle
1437  indexCount:SDL_min(maxcount, count - i) * 6
1438  indexType:MTLIndexTypeUInt16
1439  indexBuffer:data.mtlbufquadindices
1440  indexBufferOffset:0];
1441  }
1442  }
1443  break;
1444  }
1445 
1446  case SDL_RENDERCMD_COPY: {
1447  SetCopyState(renderer, cmd, CONSTANTS_OFFSET_IDENTITY, mtlbufvertex, &statecache);
1448  [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
1449  break;
1450  }
1451 
1452  case SDL_RENDERCMD_COPY_EX: {
1453  SetCopyState(renderer, cmd, CONSTANTS_OFFSET_INVALID, mtlbufvertex, &statecache);
1454  [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:cmd->data.draw.count atIndex:3]; // transform
1455  [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
1456  break;
1457  }
1458 
1459  case SDL_RENDERCMD_NO_OP:
1460  break;
1461  }
1462  cmd = cmd->next;
1463  }
1464 
1465  return 0;
1466 }}
1467 
1468 static int
1469 METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
1470  Uint32 pixel_format, void * pixels, int pitch)
1471 { @autoreleasepool {
1472  METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
1473  METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad, NULL, nil);
1474 
1475  [data.mtlcmdencoder endEncoding];
1476  id<MTLTexture> mtltexture = data.mtlpassdesc.colorAttachments[0].texture;
1477 
1478 #ifdef __MACOSX__
1479  /* on macOS with managed-storage textures, we need to tell the driver to
1480  * update the CPU-side copy of the texture data.
1481  * NOTE: Currently all of our textures are managed on macOS. We'll need some
1482  * extra copying for any private textures. */
1483  if (METAL_GetStorageMode(mtltexture) == MTLStorageModeManaged) {
1484  id<MTLBlitCommandEncoder> blit = [data.mtlcmdbuffer blitCommandEncoder];
1485  [blit synchronizeResource:mtltexture];
1486  [blit endEncoding];
1487  }
1488 #endif
1489 
1490  /* Commit the current command buffer and wait until it's completed, to make
1491  * sure the GPU has finished rendering to it by the time we read it. */
1492  [data.mtlcmdbuffer commit];
1493  [data.mtlcmdbuffer waitUntilCompleted];
1494  data.mtlcmdencoder = nil;
1495  data.mtlcmdbuffer = nil;
1496 
1497  MTLRegion mtlregion = MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h);
1498 
1499  // we only do BGRA8 or RGBA8 at the moment, so 4 will do.
1500  const int temp_pitch = rect->w * 4;
1501  void *temp_pixels = SDL_malloc(temp_pitch * rect->h);
1502  if (!temp_pixels) {
1503  return SDL_OutOfMemory();
1504  }
1505 
1506  [mtltexture getBytes:temp_pixels bytesPerRow:temp_pitch fromRegion:mtlregion mipmapLevel:0];
1507 
1508  const Uint32 temp_format = (mtltexture.pixelFormat == MTLPixelFormatBGRA8Unorm) ? SDL_PIXELFORMAT_ARGB8888 : SDL_PIXELFORMAT_ABGR8888;
1509  const int status = SDL_ConvertPixels(rect->w, rect->h, temp_format, temp_pixels, temp_pitch, pixel_format, pixels, pitch);
1510  SDL_free(temp_pixels);
1511  return status;
1512 }}
1513 
1514 static void
1515 METAL_RenderPresent(SDL_Renderer * renderer)
1516 { @autoreleasepool {
1517  METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
1518 
1519  if (data.mtlcmdencoder != nil) {
1520  [data.mtlcmdencoder endEncoding];
1521  }
1522  if (data.mtlbackbuffer != nil) {
1523  [data.mtlcmdbuffer presentDrawable:data.mtlbackbuffer];
1524  }
1525  if (data.mtlcmdbuffer != nil) {
1526  [data.mtlcmdbuffer commit];
1527  }
1528  data.mtlcmdencoder = nil;
1529  data.mtlcmdbuffer = nil;
1530  data.mtlbackbuffer = nil;
1531 }}
1532 
1533 static void
1534 METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
1535 { @autoreleasepool {
1536  CFBridgingRelease(texture->driverdata);
1537  texture->driverdata = NULL;
1538 }}
1539 
1540 static void
1541 METAL_DestroyRenderer(SDL_Renderer * renderer)
1542 { @autoreleasepool {
1543  if (renderer->driverdata) {
1544  METAL_RenderData *data = CFBridgingRelease(renderer->driverdata);
1545 
1546  if (data.mtlcmdencoder != nil) {
1547  [data.mtlcmdencoder endEncoding];
1548  }
1549 
1550  DestroyAllPipelines(data.allpipelines, data.pipelinescount);
1551 
1552  SDL_Metal_DestroyView(data.mtlview);
1553  }
1554 
1555  SDL_free(renderer);
1556 }}
1557 
1558 static void *
1559 METAL_GetMetalLayer(SDL_Renderer * renderer)
1560 { @autoreleasepool {
1561  METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
1562  return (__bridge void*)data.mtllayer;
1563 }}
1564 
1565 static void *
1566 METAL_GetMetalCommandEncoder(SDL_Renderer * renderer)
1567 { @autoreleasepool {
1568  METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad, NULL, nil);
1569  METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
1570  return (__bridge void*)data.mtlcmdencoder;
1571 }}
1572 
1573 static SDL_Renderer *
1574 METAL_CreateRenderer(SDL_Window * window, Uint32 flags)
1575 { @autoreleasepool {
1577  METAL_RenderData *data = NULL;
1578  id<MTLDevice> mtldevice = nil;
1579  SDL_MetalView view = NULL;
1580  CAMetalLayer *layer = nil;
1581  SDL_SysWMinfo syswm;
1582 
1583  SDL_VERSION(&syswm.version);
1584  if (!SDL_GetWindowWMInfo(window, &syswm)) {
1585  return NULL;
1586  }
1587 
1588  if (IsMetalAvailable(&syswm) == -1) {
1589  return NULL;
1590  }
1591 
1592  renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
1593  if (!renderer) {
1594  SDL_OutOfMemory();
1595  return NULL;
1596  }
1597 
1598  // !!! FIXME: MTLCopyAllDevices() can find other GPUs on macOS...
1599  mtldevice = MTLCreateSystemDefaultDevice();
1600 
1601  if (mtldevice == nil) {
1602  SDL_free(renderer);
1603  SDL_SetError("Failed to obtain Metal device");
1604  return NULL;
1605  }
1606 
1607  view = SDL_Metal_CreateView(window);
1608 
1609  if (view == NULL) {
1610 #if !__has_feature(objc_arc)
1611  [mtldevice release];
1612 #endif
1613  SDL_free(renderer);
1614  return NULL;
1615  }
1616 
1617  // !!! FIXME: error checking on all of this.
1618  data = [[METAL_RenderData alloc] init];
1619 
1620  if (data == nil) {
1621 #if !__has_feature(objc_arc)
1622  [mtldevice release];
1623 #endif
1624  SDL_Metal_DestroyView(view);
1625  SDL_free(renderer);
1626  return NULL;
1627  }
1628 
1629  renderer->driverdata = (void*)CFBridgingRetain(data);
1630  renderer->window = window;
1631 
1632  data.mtlview = view;
1633 
1634 #ifdef __MACOSX__
1635  layer = (CAMetalLayer *)[(NSView *)view layer];
1636 #else
1637  layer = (CAMetalLayer *)[(__bridge UIView *)view layer];
1638 #endif
1639 
1640  layer.device = mtldevice;
1641 
1642  /* Necessary for RenderReadPixels. */
1643  layer.framebufferOnly = NO;
1644 
1645  data.mtldevice = layer.device;
1646  data.mtllayer = layer;
1647  id<MTLCommandQueue> mtlcmdqueue = [data.mtldevice newCommandQueue];
1648  data.mtlcmdqueue = mtlcmdqueue;
1649  data.mtlcmdqueue.label = @"SDL Metal Renderer";
1650  data.mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor];
1651 
1652  NSError *err = nil;
1653 
1654  // The compiled .metallib is embedded in a static array in a header file
1655  // but the original shader source code is in SDL_shaders_metal.metal.
1656  dispatch_data_t mtllibdata = dispatch_data_create(sdl_metallib, sdl_metallib_len, dispatch_get_global_queue(0, 0), ^{});
1657  id<MTLLibrary> mtllibrary = [data.mtldevice newLibraryWithData:mtllibdata error:&err];
1658  data.mtllibrary = mtllibrary;
1659  SDL_assert(err == nil);
1660 #if !__has_feature(objc_arc)
1661  dispatch_release(mtllibdata);
1662 #endif
1663  data.mtllibrary.label = @"SDL Metal renderer shader library";
1664 
1665  /* Do some shader pipeline state loading up-front rather than on demand. */
1666  data.pipelinescount = 0;
1667  data.allpipelines = NULL;
1668  ChooseShaderPipelines(data, MTLPixelFormatBGRA8Unorm);
1669 
1670  MTLSamplerDescriptor *samplerdesc = [[MTLSamplerDescriptor alloc] init];
1671 
1672  samplerdesc.minFilter = MTLSamplerMinMagFilterNearest;
1673  samplerdesc.magFilter = MTLSamplerMinMagFilterNearest;
1674  id<MTLSamplerState> mtlsamplernearest = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc];
1675  data.mtlsamplernearest = mtlsamplernearest;
1676 
1677  samplerdesc.minFilter = MTLSamplerMinMagFilterLinear;
1678  samplerdesc.magFilter = MTLSamplerMinMagFilterLinear;
1679  id<MTLSamplerState> mtlsamplerlinear = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc];
1680  data.mtlsamplerlinear = mtlsamplerlinear;
1681 
1682  /* Note: matrices are column major. */
1683  float identitytransform[16] = {
1684  1.0f, 0.0f, 0.0f, 0.0f,
1685  0.0f, 1.0f, 0.0f, 0.0f,
1686  0.0f, 0.0f, 1.0f, 0.0f,
1687  0.0f, 0.0f, 0.0f, 1.0f,
1688  };
1689 
1690  float halfpixeltransform[16] = {
1691  1.0f, 0.0f, 0.0f, 0.0f,
1692  0.0f, 1.0f, 0.0f, 0.0f,
1693  0.0f, 0.0f, 1.0f, 0.0f,
1694  0.5f, 0.5f, 0.0f, 1.0f,
1695  };
1696 
1697  /* Metal pads float3s to 16 bytes. */
1698  float decodetransformJPEG[4*4] = {
1699  0.0, -0.501960814, -0.501960814, 0.0, /* offset */
1700  1.0000, 0.0000, 1.4020, 0.0, /* Rcoeff */
1701  1.0000, -0.3441, -0.7141, 0.0, /* Gcoeff */
1702  1.0000, 1.7720, 0.0000, 0.0, /* Bcoeff */
1703  };
1704 
1705  float decodetransformBT601[4*4] = {
1706  -0.0627451017, -0.501960814, -0.501960814, 0.0, /* offset */
1707  1.1644, 0.0000, 1.5960, 0.0, /* Rcoeff */
1708  1.1644, -0.3918, -0.8130, 0.0, /* Gcoeff */
1709  1.1644, 2.0172, 0.0000, 0.0, /* Bcoeff */
1710  };
1711 
1712  float decodetransformBT709[4*4] = {
1713  0.0, -0.501960814, -0.501960814, 0.0, /* offset */
1714  1.0000, 0.0000, 1.4020, 0.0, /* Rcoeff */
1715  1.0000, -0.3441, -0.7141, 0.0, /* Gcoeff */
1716  1.0000, 1.7720, 0.0000, 0.0, /* Bcoeff */
1717  };
1718 
1719  id<MTLBuffer> mtlbufconstantstaging = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModeShared];
1720  #if !__has_feature(objc_arc)
1721  [mtlbufconstantstaging autorelease];
1722  #endif
1723 
1724  char *constantdata = [mtlbufconstantstaging contents];
1725  SDL_memcpy(constantdata + CONSTANTS_OFFSET_IDENTITY, identitytransform, sizeof(identitytransform));
1726  SDL_memcpy(constantdata + CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM, halfpixeltransform, sizeof(halfpixeltransform));
1727  SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_JPEG, decodetransformJPEG, sizeof(decodetransformJPEG));
1728  SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT601, decodetransformBT601, sizeof(decodetransformBT601));
1729  SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT709, decodetransformBT709, sizeof(decodetransformBT709));
1730 
1731  int quadcount = UINT16_MAX / 4;
1732  size_t indicessize = sizeof(UInt16) * quadcount * 6;
1733  id<MTLBuffer> mtlbufquadindicesstaging = [data.mtldevice newBufferWithLength:indicessize options:MTLResourceStorageModeShared];
1734 #if !__has_feature(objc_arc)
1735  [mtlbufquadindicesstaging autorelease];
1736 #endif
1737 
1738  /* Quads in the following vertex order (matches the FillRects vertices):
1739  * 1---3
1740  * | \ |
1741  * 0---2
1742  */
1743  UInt16 *indexdata = [mtlbufquadindicesstaging contents];
1744  for (int i = 0; i < quadcount; i++) {
1745  indexdata[i * 6 + 0] = i * 4 + 0;
1746  indexdata[i * 6 + 1] = i * 4 + 1;
1747  indexdata[i * 6 + 2] = i * 4 + 2;
1748 
1749  indexdata[i * 6 + 3] = i * 4 + 2;
1750  indexdata[i * 6 + 4] = i * 4 + 1;
1751  indexdata[i * 6 + 5] = i * 4 + 3;
1752  }
1753 
1754  id<MTLBuffer> mtlbufconstants = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModePrivate];
1755  data.mtlbufconstants = mtlbufconstants;
1756  data.mtlbufconstants.label = @"SDL constant data";
1757 
1758  id<MTLBuffer> mtlbufquadindices = [data.mtldevice newBufferWithLength:indicessize options:MTLResourceStorageModePrivate];
1759  data.mtlbufquadindices = mtlbufquadindices;
1760  data.mtlbufquadindices.label = @"SDL quad index buffer";
1761 
1762  id<MTLCommandBuffer> cmdbuffer = [data.mtlcmdqueue commandBuffer];
1763  id<MTLBlitCommandEncoder> blitcmd = [cmdbuffer blitCommandEncoder];
1764 
1765  [blitcmd copyFromBuffer:mtlbufconstantstaging sourceOffset:0 toBuffer:mtlbufconstants destinationOffset:0 size:CONSTANTS_LENGTH];
1766  [blitcmd copyFromBuffer:mtlbufquadindicesstaging sourceOffset:0 toBuffer:mtlbufquadindices destinationOffset:0 size:indicessize];
1767 
1768  [blitcmd endEncoding];
1769  [cmdbuffer commit];
1770 
1771  // !!! FIXME: force more clears here so all the drawables are sane to start, and our static buffers are definitely flushed.
1772 
1773  renderer->WindowEvent = METAL_WindowEvent;
1774  renderer->GetOutputSize = METAL_GetOutputSize;
1775  renderer->SupportsBlendMode = METAL_SupportsBlendMode;
1776  renderer->CreateTexture = METAL_CreateTexture;
1777  renderer->UpdateTexture = METAL_UpdateTexture;
1778  renderer->UpdateTextureYUV = METAL_UpdateTextureYUV;
1779  renderer->LockTexture = METAL_LockTexture;
1780  renderer->UnlockTexture = METAL_UnlockTexture;
1781  renderer->SetTextureScaleMode = METAL_SetTextureScaleMode;
1782  renderer->SetRenderTarget = METAL_SetRenderTarget;
1783  renderer->QueueSetViewport = METAL_QueueSetViewport;
1784  renderer->QueueSetDrawColor = METAL_QueueSetDrawColor;
1785  renderer->QueueDrawPoints = METAL_QueueDrawPoints;
1786  renderer->QueueDrawLines = METAL_QueueDrawPoints; // lines and points queue the same way.
1787  renderer->QueueFillRects = METAL_QueueFillRects;
1788  renderer->QueueCopy = METAL_QueueCopy;
1789  renderer->QueueCopyEx = METAL_QueueCopyEx;
1790  renderer->RunCommandQueue = METAL_RunCommandQueue;
1791  renderer->RenderReadPixels = METAL_RenderReadPixels;
1792  renderer->RenderPresent = METAL_RenderPresent;
1793  renderer->DestroyTexture = METAL_DestroyTexture;
1794  renderer->DestroyRenderer = METAL_DestroyRenderer;
1795  renderer->GetMetalLayer = METAL_GetMetalLayer;
1796  renderer->GetMetalCommandEncoder = METAL_GetMetalCommandEncoder;
1797 
1800 
1802 
1803 #if defined(__MACOSX__) && defined(MAC_OS_X_VERSION_10_13)
1804  if (@available(macOS 10.13, *)) {
1805  data.mtllayer.displaySyncEnabled = (flags & SDL_RENDERER_PRESENTVSYNC) != 0;
1806  if (data.mtllayer.displaySyncEnabled) {
1808  }
1809  } else
1810 #endif
1811  {
1813  }
1814 
1815  /* https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */
1816  int maxtexsize = 4096;
1817 #if defined(__MACOSX__)
1818  maxtexsize = 16384;
1819 #elif defined(__TVOS__)
1820  maxtexsize = 8192;
1821 #ifdef __TVOS_11_0
1822  if (@available(tvOS 11.0, *)) {
1823  if ([mtldevice supportsFeatureSet:MTLFeatureSet_tvOS_GPUFamily2_v1]) {
1824  maxtexsize = 16384;
1825  }
1826  }
1827 #endif
1828 #else
1829 #ifdef __IPHONE_11_0
1830 #pragma clang diagnostic push
1831 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
1832  if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily4_v1]) {
1833  maxtexsize = 16384;
1834  } else
1835 #pragma clang diagnostic pop
1836 #endif
1837 #ifdef __IPHONE_10_0
1838  if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
1839  maxtexsize = 16384;
1840  } else
1841 #endif
1842  if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v2] || [mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v2]) {
1843  maxtexsize = 8192;
1844  } else {
1845  maxtexsize = 4096;
1846  }
1847 #endif
1848 
1849  renderer->info.max_texture_width = maxtexsize;
1850  renderer->info.max_texture_height = maxtexsize;
1851 
1852 #if !__has_feature(objc_arc)
1853  [mtlcmdqueue release];
1854  [mtllibrary release];
1855  [samplerdesc release];
1856  [mtlsamplernearest release];
1857  [mtlsamplerlinear release];
1858  [mtlbufconstants release];
1859  [mtlbufquadindices release];
1860  [data release];
1861  [mtldevice release];
1862 #endif
1863 
1864  return renderer;
1865 }}
1866 
1868  METAL_CreateRenderer,
1869  {
1870  "metal",
1872  6,
1873  {
1880  },
1881  0, 0,
1882  }
1883 };
1884 
1885 #endif /* SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED */
1886 
1887 /* vi: set ts=4 sw=4 expandtab: */
SDL_zero
#define SDL_zero(x)
Definition: SDL_stdinc.h:418
points
GLfixed GLfixed GLint GLint GLfixed points
Definition: SDL_opengl_glext.h:4561
Uint8
uint8_t Uint8
Definition: SDL_stdinc.h:179
SDL_memset
#define SDL_memset
Definition: SDL_dynapi_overrides.h:386
SDL_BLENDOPERATION_ADD
@ SDL_BLENDOPERATION_ADD
Definition: SDL_blendmode.h:67
c
const GLubyte * c
Definition: SDL_opengl_glext.h:11096
SDL_GetPixelFormatName
#define SDL_GetPixelFormatName
Definition: SDL_dynapi_overrides.h:277
offset
GLintptr offset
Definition: SDL_opengl_glext.h:541
SDL_BLENDFACTOR_DST_COLOR
@ SDL_BLENDFACTOR_DST_COLOR
Definition: SDL_blendmode.h:86
blendMode
static SDL_BlendMode blendMode
Definition: testdraw2.c:34
SDL_BLENDFACTOR_SRC_COLOR
@ SDL_BLENDFACTOR_SRC_COLOR
Definition: SDL_blendmode.h:82
SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR
@ SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR
Definition: SDL_blendmode.h:83
SDL_Renderer::GetMetalLayer
void *(* GetMetalLayer)(SDL_Renderer *renderer)
Definition: SDL_sysrender.h:154
if
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 skip1(dst_w_bpp<=(lowbit *8)) &&((lowbit *8)<(pixblock_size *dst_w_bpp)) .if lowbit< 16 tst DST_R
Definition: pixman-arm-neon-asm.h:469
SDL_Renderer::QueueCopyEx
int(* QueueCopyEx)(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, const SDL_Rect *srcquad, const SDL_FRect *dstrect, const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
Definition: SDL_sysrender.h:127
SDL_AllocateRenderVertices
void * SDL_AllocateRenderVertices(SDL_Renderer *renderer, const size_t numbytes, const size_t alignment, size_t *offset)
Definition: SDL_render.c:263
SDL_Renderer::RunCommandQueue
int(* RunCommandQueue)(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
Definition: SDL_sysrender.h:130
SDL_PIXELFORMAT_NV21
@ SDL_PIXELFORMAT_NV21
Definition: SDL_pixels.h:292
NULL
#define NULL
Definition: begin_code.h:167
layer
GLenum GLuint GLint GLint layer
Definition: SDL_opengl_glext.h:1189
b
GLboolean GLboolean GLboolean b
Definition: SDL_opengl_glext.h:1112
SDL_Renderer::SetRenderTarget
int(* SetRenderTarget)(SDL_Renderer *renderer, SDL_Texture *texture)
Definition: SDL_sysrender.h:143
g
GLboolean GLboolean g
Definition: SDL_opengl_glext.h:1112
mode
GLenum mode
Definition: SDL_opengl_glext.h:1125
SDL_VERSION
#define SDL_VERSION(x)
Macro to determine SDL version program was compiled against.
Definition: SDL_version.h:79
SDL_SysWMinfo
Definition: SDL_syswm.h:202
SDL_zerop
#define SDL_zerop(x)
Definition: SDL_stdinc.h:419
SDL_log.h
SDL_BLENDFACTOR_SRC_ALPHA
@ SDL_BLENDFACTOR_SRC_ALPHA
Definition: SDL_blendmode.h:84
SDL_BLENDMODE_BLEND
@ SDL_BLENDMODE_BLEND
Definition: SDL_blendmode.h:44
count
GLuint GLuint GLsizei count
Definition: SDL_opengl.h:1571
SDL_RenderCommand::next
struct SDL_RenderCommand * next
Definition: SDL_sysrender.h:104
r
GLdouble GLdouble GLdouble r
Definition: SDL_opengl.h:2079
SDL_BlendOperation
SDL_BlendOperation
The blend operation used when combining source and destination pixel components.
Definition: SDL_blendmode.h:66
SDL_realloc
#define SDL_realloc
Definition: SDL_dynapi_overrides.h:376
viewport
static SDL_Rect viewport
Definition: testviewport.c:28
SDL_RenderDriver
Definition: SDL_sysrender.h:223
SDL_FRect::x
float x
Definition: SDL_rect.h:89
Uint32
uint32_t Uint32
Definition: SDL_stdinc.h:203
SDL_Renderer::WindowEvent
void(* WindowEvent)(SDL_Renderer *renderer, const SDL_WindowEvent *event)
Definition: SDL_sysrender.h:113
SDL_BlendFactor
SDL_BlendFactor
The normalized factor used to multiply pixel components.
Definition: SDL_blendmode.h:79
SDL_BLENDFACTOR_DST_ALPHA
@ SDL_BLENDFACTOR_DST_ALPHA
Definition: SDL_blendmode.h:88
a
GLboolean GLboolean GLboolean GLboolean a
Definition: SDL_opengl_glext.h:1112
h
GLfloat GLfloat GLfloat GLfloat h
Definition: SDL_opengl_glext.h:1949
SDL_Rect::x
int x
Definition: SDL_rect.h:79
SDL_RENDERCMD_COPY
@ SDL_RENDERCMD_COPY
Definition: SDL_sysrender.h:76
SDL_BLENDFACTOR_ZERO
@ SDL_BLENDFACTOR_ZERO
Definition: SDL_blendmode.h:80
SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA
@ SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA
Definition: SDL_blendmode.h:89
shader
GLuint shader
Definition: SDL_opengl_glext.h:662
SDL_RENDERCMD_SETCLIPRECT
@ SDL_RENDERCMD_SETCLIPRECT
Definition: SDL_sysrender.h:70
SDL_PIXELFORMAT_IYUV
@ SDL_PIXELFORMAT_IYUV
Definition: SDL_pixels.h:282
SDL_Metal_DestroyView
#define SDL_Metal_DestroyView
Definition: SDL_dynapi_overrides.h:728
SDL_MetalView
void * SDL_MetalView
A handle to a CAMetalLayer-backed NSView (macOS) or UIView (iOS/tvOS).
Definition: SDL_metal.h:44
data
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
SDL_Rect::w
int w
Definition: SDL_rect.h:80
SDL_BLENDOPERATION_MAXIMUM
@ SDL_BLENDOPERATION_MAXIMUM
Definition: SDL_blendmode.h:71
SDL_BLENDOPERATION_SUBTRACT
@ SDL_BLENDOPERATION_SUBTRACT
Definition: SDL_blendmode.h:68
SDL_Window
The type used to identify a window.
Definition: SDL_sysvideo.h:75
SDL_YUV_CONVERSION_JPEG
@ SDL_YUV_CONVERSION_JPEG
Definition: SDL_surface.h:106
SDL_WINDOWEVENT_SHOWN
@ SDL_WINDOWEVENT_SHOWN
Definition: SDL_video.h:148
len
GLenum GLsizei len
Definition: SDL_opengl_glext.h:2929
SDL_memcpy
#define SDL_memcpy
Definition: SDL_dynapi_overrides.h:387
event
struct _cl_event * event
Definition: SDL_opengl_glext.h:2652
SDL_Renderer
Definition: SDL_sysrender.h:110
SDL_Renderer::QueueSetDrawColor
int(* QueueSetDrawColor)(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
Definition: SDL_sysrender.h:118
usage
GLsizeiptr const void GLenum usage
Definition: SDL_opengl_glext.h:540
SDL_FPoint
The structure that defines a point (floating point)
Definition: SDL_rect.h:61
SDL_Renderer::driverdata
void * driverdata
Definition: SDL_sysrender.h:218
SDL_FRect::y
float y
Definition: SDL_rect.h:90
SDL_RenderCommand::color
struct SDL_RenderCommand::@23::@27 color
SDL_SYSWM_COCOA
@ SDL_SYSWM_COCOA
Definition: SDL_syswm.h:128
x
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1574
SDL_SysWMinfo::subsystem
SDL_SYSWM_TYPE subsystem
Definition: SDL_syswm.h:204
color
GLuint color
Definition: SDL_opengl_glext.h:1151
window
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
label
GLuint GLsizei const GLchar * label
Definition: SDL_opengl_glext.h:2488
SDL_Renderer::SetTextureScaleMode
void(* SetTextureScaleMode)(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ScaleMode scaleMode)
Definition: SDL_sysrender.h:142
SDL_RendererInfo::flags
Uint32 flags
Definition: SDL_render.h:81
SDL_Rect::y
int y
Definition: SDL_rect.h:79
SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR
@ SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR
Definition: SDL_blendmode.h:87
SDL_Rect::h
int h
Definition: SDL_rect.h:80
SDL_free
#define SDL_free
Definition: SDL_dynapi_overrides.h:377
f
GLfloat f
Definition: SDL_opengl_glext.h:1873
SDL_BLENDMODE_NONE
@ SDL_BLENDMODE_NONE
Definition: SDL_blendmode.h:42
SDL_Renderer::SupportsBlendMode
SDL_bool(* SupportsBlendMode)(SDL_Renderer *renderer, SDL_BlendMode blendMode)
Definition: SDL_sysrender.h:115
SDL_RENDERCMD_SETVIEWPORT
@ SDL_RENDERCMD_SETVIEWPORT
Definition: SDL_sysrender.h:69
SDL_Renderer::QueueDrawLines
int(* QueueDrawLines)(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count)
Definition: SDL_sysrender.h:121
SDL_RENDERER_PRESENTVSYNC
@ SDL_RENDERER_PRESENTVSYNC
Definition: SDL_render.h:69
SDL_ScaleMode
SDL_ScaleMode
The scaling mode for a texture.
Definition: SDL_render.h:92
SDL_RENDERCMD_DRAW_POINTS
@ SDL_RENDERCMD_DRAW_POINTS
Definition: SDL_sysrender.h:73
rect
SDL_Rect rect
Definition: testrelative.c:27
SDL_YUV_CONVERSION_MODE
SDL_YUV_CONVERSION_MODE
The formula used for converting between YUV and RGB.
Definition: SDL_surface.h:105
SDL_assert.h
SetDrawState
static void SetDrawState(SDL_Surface *surface, SW_DrawStateCache *drawstate)
Definition: SDL_render_sw.c:589
SDL_Renderer::always_batch
SDL_bool always_batch
Definition: SDL_sysrender.h:200
SDL_SYSWM_UIKIT
@ SDL_SYSWM_UIKIT
Definition: SDL_syswm.h:129
SDL_GetBlendModeDstAlphaFactor
SDL_BlendFactor SDL_GetBlendModeDstAlphaFactor(SDL_BlendMode blendMode)
Definition: SDL_render.c:3391
SDL_RenderCommand::command
SDL_RenderCommandType command
Definition: SDL_sysrender.h:82
SDL_TRUE
@ SDL_TRUE
Definition: SDL_stdinc.h:164
SDL_RENDERCMD_NO_OP
@ SDL_RENDERCMD_NO_OP
Definition: SDL_sysrender.h:68
SDL_Renderer::QueueSetViewport
int(* QueueSetViewport)(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
Definition: SDL_sysrender.h:117
SDL_PIXELFORMAT_ARGB8888
@ SDL_PIXELFORMAT_ARGB8888
Definition: SDL_pixels.h:251
SDL_assert
#define SDL_assert(condition)
Definition: SDL_assert.h:169
SDL_BLENDMODE_MUL
@ SDL_BLENDMODE_MUL
Definition: SDL_blendmode.h:53
SDL_RENDERCMD_DRAW_LINES
@ SDL_RENDERCMD_DRAW_LINES
Definition: SDL_sysrender.h:74
pixels
GLint GLint GLsizei GLsizei GLsizei GLint GLenum GLenum const GLvoid * pixels
Definition: SDL_opengl.h:1572
SDL_Renderer::GetOutputSize
int(* GetOutputSize)(SDL_Renderer *renderer, int *w, int *h)
Definition: SDL_sysrender.h:114
SDL_OutOfMemory
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_FLIP_VERTICAL
@ SDL_FLIP_VERTICAL
Definition: SDL_render.h:125
SDL_Renderer::CreateTexture
int(* CreateTexture)(SDL_Renderer *renderer, SDL_Texture *texture)
Definition: SDL_sysrender.h:116
SDL_RenderCommand::data
union SDL_RenderCommand::@23 data
y
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1574
SDL_RENDERCMD_FILL_RECTS
@ SDL_RENDERCMD_FILL_RECTS
Definition: SDL_sysrender.h:75
available
static int available()
Definition: video.c:356
id
GLuint id
Definition: SDL_opengl_glext.h:531
SDL_Renderer::DestroyRenderer
void(* DestroyRenderer)(SDL_Renderer *renderer)
Definition: SDL_sysrender.h:149
SDL_Renderer::LockTexture
int(* LockTexture)(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, void **pixels, int *pitch)
Definition: SDL_sysrender.h:139
SDL_Renderer::GetMetalCommandEncoder
void *(* GetMetalCommandEncoder)(SDL_Renderer *renderer)
Definition: SDL_sysrender.h:155
SDL_ScaleModeNearest
@ SDL_ScaleModeNearest
Definition: SDL_render.h:93
SDL_calloc
#define SDL_calloc
Definition: SDL_dynapi_overrides.h:375
first
const GLint * first
Definition: SDL_opengl_glext.h:371
SDL_Renderer::RenderReadPixels
int(* RenderReadPixels)(SDL_Renderer *renderer, const SDL_Rect *rect, Uint32 format, void *pixels, int pitch)
Definition: SDL_sysrender.h:144
SDL_GetBlendModeSrcAlphaFactor
SDL_BlendFactor SDL_GetBlendModeSrcAlphaFactor(SDL_BlendMode blendMode)
Definition: SDL_render.c:3384
SDL_GetBlendModeColorOperation
SDL_BlendOperation SDL_GetBlendModeColorOperation(SDL_BlendMode blendMode)
Definition: SDL_render.c:3377
SDL_RENDERCMD_COPY_EX
@ SDL_RENDERCMD_COPY_EX
Definition: SDL_sysrender.h:77
SDL_GetWindowWMInfo
#define SDL_GetWindowWMInfo
Definition: SDL_dynapi_overrides.h:473
SDL_BLENDOPERATION_REV_SUBTRACT
@ SDL_BLENDOPERATION_REV_SUBTRACT
Definition: SDL_blendmode.h:69
SDL_RenderCommand
Definition: SDL_sysrender.h:81
renderer
static SDL_Renderer * renderer
Definition: testaudiocapture.c:21
SDL_GetBlendModeDstColorFactor
SDL_BlendFactor SDL_GetBlendModeDstColorFactor(SDL_BlendMode blendMode)
Definition: SDL_render.c:3370
SDL_RenderCommand::cliprect
struct SDL_RenderCommand::@23::@25 cliprect
SDL_SysWMinfo::version
SDL_version version
Definition: SDL_syswm.h:203
SDL_BLENDFACTOR_ONE
@ SDL_BLENDFACTOR_ONE
Definition: SDL_blendmode.h:81
SDL_Renderer::info
SDL_RendererInfo info
Definition: SDL_sysrender.h:158
SDL_RENDERER_TARGETTEXTURE
@ SDL_RENDERER_TARGETTEXTURE
Definition: SDL_render.h:71
SDL_SetError
#define SDL_SetError
Definition: SDL_dynapi_overrides.h:30
SDL_Renderer::RenderPresent
void(* RenderPresent)(SDL_Renderer *renderer)
Definition: SDL_sysrender.h:146
SDL_BYTESPERPIXEL
#define SDL_BYTESPERPIXEL(X)
Definition: SDL_pixels.h:128
s
GLdouble s
Definition: SDL_opengl.h:2063
SDL_Metal_CreateView
#define SDL_Metal_CreateView
Definition: SDL_dynapi_overrides.h:727
SDL_Rect
A rectangle, with the origin at the upper left (integer).
Definition: SDL_rect.h:78
SDL_RendererInfo::max_texture_width
int max_texture_width
Definition: SDL_render.h:84
METAL_RenderDriver
SDL_RenderDriver METAL_RenderDriver
SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA
@ SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA
Definition: SDL_blendmode.h:85
pipelines
const GLuint * pipelines
Definition: SDL_opengl_glext.h:1882
SDL_Texture
Definition: SDL_sysrender.h:37
SDL_Renderer::QueueFillRects
int(* QueueFillRects)(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FRect *rects, int count)
Definition: SDL_sysrender.h:123
SDL_Renderer::UpdateTexture
int(* UpdateTexture)(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch)
Definition: SDL_sysrender.h:131
SDL_hints.h
SDL_RendererFlip
SDL_RendererFlip
Flip constants for SDL_RenderCopyEx.
Definition: SDL_render.h:122
SDL_PIXELFORMAT_NV12
@ SDL_PIXELFORMAT_NV12
Definition: SDL_pixels.h:290
SDL_TEXTUREACCESS_TARGET
@ SDL_TEXTUREACCESS_TARGET
Definition: SDL_render.h:105
SDL_WindowEvent
Window state change event data (event.window.*)
Definition: SDL_events.h:196
SDL_FRect
A rectangle, with the origin at the upper left (floating point).
Definition: SDL_rect.h:88
SDL_RenderDriver::info
SDL_RendererInfo info
Definition: SDL_sysrender.h:227
SDL_ConvertPixels
#define SDL_ConvertPixels
Definition: SDL_dynapi_overrides.h:465
angle
GLfloat angle
Definition: SDL_opengl_glext.h:6100
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:162
SDL_metal.h
SDL_GetYUVConversionModeForResolution
#define SDL_GetYUVConversionModeForResolution
Definition: SDL_dynapi_overrides.h:665
SDL_WINDOWEVENT_HIDDEN
@ SDL_WINDOWEVENT_HIDDEN
Definition: SDL_video.h:149
SDL_Renderer::QueueDrawPoints
int(* QueueDrawPoints)(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count)
Definition: SDL_sysrender.h:119
SDL_FALSE
@ SDL_FALSE
Definition: SDL_stdinc.h:163
matrix
GLuint GLenum matrix
Definition: SDL_opengl_glext.h:9999
SDL_YUV_CONVERSION_BT709
@ SDL_YUV_CONVERSION_BT709
Definition: SDL_surface.h:108
SDL_malloc
#define SDL_malloc
Definition: SDL_dynapi_overrides.h:374
SDL_RENDERER_ACCELERATED
@ SDL_RENDERER_ACCELERATED
Definition: SDL_render.h:67
SDL_RendererInfo::max_texture_height
int max_texture_height
Definition: SDL_render.h:85
void
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
Definition: SDL_dynapi_procs.h:89
SDL_PIXELFORMAT_YV12
@ SDL_PIXELFORMAT_YV12
Definition: SDL_pixels.h:280
flags
GLbitfield flags
Definition: SDL_opengl_glext.h:1483
SDL_RENDERCMD_SETDRAWCOLOR
@ SDL_RENDERCMD_SETDRAWCOLOR
Definition: SDL_sysrender.h:71
SDL_Renderer::QueueCopy
int(* QueueCopy)(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, const SDL_Rect *srcrect, const SDL_FRect *dstrect)
Definition: SDL_sysrender.h:125
SDL_Renderer::window
SDL_Window * window
Definition: SDL_sysrender.h:161
SDL_Renderer::target
SDL_Texture * target
Definition: SDL_sysrender.h:194
texture
GLenum GLenum GLuint texture
Definition: SDL_opengl_glext.h:1181
SDL_RenderCommand::viewport
struct SDL_RenderCommand::@23::@24 viewport
rects
EGLSurface EGLint * rects
Definition: eglext.h:282
SDL_PIXELFORMAT_ABGR8888
@ SDL_PIXELFORMAT_ABGR8888
Definition: SDL_pixels.h:257
SDL_GetBlendModeAlphaOperation
SDL_BlendOperation SDL_GetBlendModeAlphaOperation(SDL_BlendMode blendMode)
Definition: SDL_render.c:3398
state
struct xkb_state * state
Definition: SDL_waylandsym.h:114
SDL_BLENDOPERATION_MINIMUM
@ SDL_BLENDOPERATION_MINIMUM
Definition: SDL_blendmode.h:70
SDL_RenderCommand::draw
struct SDL_RenderCommand::@23::@26 draw
SDL_GetBlendModeSrcColorFactor
SDL_BlendFactor SDL_GetBlendModeSrcColorFactor(SDL_BlendMode blendMode)
Definition: SDL_render.c:3363
SDL_BLENDMODE_MOD
@ SDL_BLENDMODE_MOD
Definition: SDL_blendmode.h:50
i
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
SDL_RENDERCMD_CLEAR
@ SDL_RENDERCMD_CLEAR
Definition: SDL_sysrender.h:72
SDL_YUV_CONVERSION_BT601
@ SDL_YUV_CONVERSION_BT601
Definition: SDL_surface.h:107
SDL_BLENDMODE_ADD
@ SDL_BLENDMODE_ADD
Definition: SDL_blendmode.h:47
SDL_BlendMode
SDL_BlendMode
The blend mode used in SDL_RenderCopy() and drawing operations.
Definition: SDL_blendmode.h:41
SDL_Texture::driverdata
void * driverdata
Definition: SDL_sysrender.h:60
SDL_Renderer::UnlockTexture
void(* UnlockTexture)(SDL_Renderer *renderer, SDL_Texture *texture)
Definition: SDL_sysrender.h:141
SDL_syswm.h
SDL_Renderer::DestroyTexture
void(* DestroyTexture)(SDL_Renderer *renderer, SDL_Texture *texture)
Definition: SDL_sysrender.h:147
SDL_Renderer::UpdateTextureYUV
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)
Definition: SDL_sysrender.h:134
w
GLubyte GLubyte GLubyte GLubyte w
Definition: SDL_opengl_glext.h:734
SDL_FLIP_HORIZONTAL
@ SDL_FLIP_HORIZONTAL
Definition: SDL_render.h:124