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