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