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