21 #include "../../SDL_internal.h"
23 #if SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED
29 #include "../SDL_sysrender.h"
32 #include "../../video/cocoa/SDL_cocoametalview.h"
34 #include "../../video/uikit/SDL_uikitmetalview.h"
36 #include <Availability.h>
37 #import <Metal/Metal.h>
38 #import <QuartzCore/CAMetalLayer.h>
51 #define CONSTANT_ALIGN 256
53 #define CONSTANT_ALIGN 4
56 #define ALIGN_CONSTANTS(size) ((size + CONSTANT_ALIGN - 1) & (~(CONSTANT_ALIGN - 1)))
58 static const size_t CONSTANTS_OFFSET_INVALID = 0xFFFFFFFF;
59 static const size_t CONSTANTS_OFFSET_IDENTITY = 0;
60 static const size_t CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM = ALIGN_CONSTANTS(CONSTANTS_OFFSET_IDENTITY +
sizeof(
float) * 16);
61 static const size_t CONSTANTS_OFFSET_DECODE_JPEG = ALIGN_CONSTANTS(CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM +
sizeof(
float) * 16);
62 static const size_t CONSTANTS_OFFSET_DECODE_BT601 = ALIGN_CONSTANTS(CONSTANTS_OFFSET_DECODE_JPEG +
sizeof(
float) * 4 * 4);
63 static const size_t CONSTANTS_OFFSET_DECODE_BT709 = ALIGN_CONSTANTS(CONSTANTS_OFFSET_DECODE_BT601 +
sizeof(
float) * 4 * 4);
64 static const size_t CONSTANTS_LENGTH = CONSTANTS_OFFSET_DECODE_BT709 + sizeof(float) * 4 * 4;
66 typedef enum SDL_MetalVertexFunction
68 SDL_METAL_VERTEX_SOLID,
69 SDL_METAL_VERTEX_COPY,
70 } SDL_MetalVertexFunction;
72 typedef enum SDL_MetalFragmentFunction
74 SDL_METAL_FRAGMENT_SOLID = 0,
75 SDL_METAL_FRAGMENT_COPY,
76 SDL_METAL_FRAGMENT_YUV,
77 SDL_METAL_FRAGMENT_NV12,
78 SDL_METAL_FRAGMENT_NV21,
79 SDL_METAL_FRAGMENT_COUNT,
80 } SDL_MetalFragmentFunction;
82 typedef struct METAL_PipelineState
86 } METAL_PipelineState;
88 typedef struct METAL_PipelineCache
90 METAL_PipelineState *states;
92 SDL_MetalVertexFunction vertexFunction;
93 SDL_MetalFragmentFunction fragmentFunction;
94 MTLPixelFormat renderTargetFormat;
96 } METAL_PipelineCache;
104 typedef struct METAL_ShaderPipelines
106 MTLPixelFormat renderTargetFormat;
107 METAL_PipelineCache caches[SDL_METAL_FRAGMENT_COUNT];
108 } METAL_ShaderPipelines;
110 @interface METAL_RenderData : NSObject
120 @property (nonatomic, retain)
id<MTLBuffer> mtlbufquadindices;
121 @property (nonatomic, retain) CAMetalLayer *mtllayer;
122 @property (nonatomic, retain) MTLRenderPassDescriptor *mtlpassdesc;
123 @property (nonatomic, assign) METAL_ShaderPipelines *activepipelines;
124 @property (nonatomic, assign) METAL_ShaderPipelines *allpipelines;
125 @property (nonatomic, assign)
int pipelinescount;
128 @implementation METAL_RenderData
129 #if !__has_feature(objc_arc)
132 [_mtldevice release];
133 [_mtlcmdqueue release];
134 [_mtlcmdbuffer release];
135 [_mtlcmdencoder release];
136 [_mtllibrary release];
137 [_mtlbackbuffer release];
138 [_mtlsamplernearest release];
139 [_mtlsamplerlinear release];
140 [_mtlbufconstants release];
141 [_mtlbufquadindices release];
143 [_mtlpassdesc release];
149 @interface METAL_TextureData : NSObject
153 @property (nonatomic, assign) SDL_MetalFragmentFunction fragmentFunction;
154 @property (nonatomic, assign) BOOL yuv;
155 @property (nonatomic, assign) BOOL nv12;
156 @property (nonatomic, assign)
size_t conversionBufferOffset;
157 @property (nonatomic, assign) BOOL hasdata;
160 @property (nonatomic, assign)
SDL_Rect lockedrect;
163 @implementation METAL_TextureData
164 #if !__has_feature(objc_arc)
167 [_mtltexture release];
168 [_mtltexture_uv release];
169 [_mtlsampler release];
179 return SDL_SetError(
"Metal render target only supports Cocoa and UIKit video targets at the moment.");
183 #if (defined(__MACOSX__) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101100))
184 if (MTLCreateSystemDefaultDevice ==
NULL) {
185 return SDL_SetError(
"Metal framework not available on this system");
192 static const MTLBlendOperation invalidBlendOperation = (MTLBlendOperation)0xFFFFFFFF;
193 static const MTLBlendFactor invalidBlendFactor = (MTLBlendFactor)0xFFFFFFFF;
195 static MTLBlendOperation
204 default:
return invalidBlendOperation;
208 static MTLBlendFactor
222 default:
return invalidBlendFactor;
227 GetVertexFunctionName(SDL_MetalVertexFunction
function)
230 case SDL_METAL_VERTEX_SOLID:
return @"SDL_Solid_vertex";
231 case SDL_METAL_VERTEX_COPY:
return @"SDL_Copy_vertex";
237 GetFragmentFunctionName(SDL_MetalFragmentFunction
function)
240 case SDL_METAL_FRAGMENT_SOLID:
return @"SDL_Solid_fragment";
241 case SDL_METAL_FRAGMENT_COPY:
return @"SDL_Copy_fragment";
242 case SDL_METAL_FRAGMENT_YUV:
return @"SDL_YUV_fragment";
243 case SDL_METAL_FRAGMENT_NV12:
return @"SDL_NV12_fragment";
244 case SDL_METAL_FRAGMENT_NV21:
return @"SDL_NV21_fragment";
250 MakePipelineState(METAL_RenderData *
data, METAL_PipelineCache *cache,
253 id<MTLFunction> mtlvertfn = [data.mtllibrary newFunctionWithName:GetVertexFunctionName(cache->vertexFunction)];
254 id<MTLFunction> mtlfragfn = [data.mtllibrary newFunctionWithName:GetFragmentFunctionName(cache->fragmentFunction)];
258 MTLRenderPipelineDescriptor *mtlpipedesc = [[MTLRenderPipelineDescriptor alloc] init];
259 mtlpipedesc.vertexFunction = mtlvertfn;
260 mtlpipedesc.fragmentFunction = mtlfragfn;
262 MTLRenderPipelineColorAttachmentDescriptor *rtdesc = mtlpipedesc.colorAttachments[0];
264 rtdesc.pixelFormat = cache->renderTargetFormat;
267 rtdesc.blendingEnabled = YES;
275 rtdesc.blendingEnabled = NO;
278 mtlpipedesc.label = [@(cache->label) stringByAppendingString:blendlabel];
284 METAL_PipelineState pipeline;
285 pipeline.blendMode = blendmode;
286 pipeline.pipe = (
void *)CFBridgingRetain(
state);
288 METAL_PipelineState *states =
SDL_realloc(cache->states, (cache->count + 1) *
sizeof(pipeline));
290 #if !__has_feature(objc_arc)
291 [mtlpipedesc release];
298 states[cache->count++] = pipeline;
299 cache->states = states;
302 CFBridgingRelease(pipeline.pipe);
309 MakePipelineCache(METAL_RenderData *
data, METAL_PipelineCache *cache,
const char *
label,
310 MTLPixelFormat rtformat, SDL_MetalVertexFunction vertfn, SDL_MetalFragmentFunction fragfn)
314 cache->vertexFunction = vertfn;
315 cache->fragmentFunction = fragfn;
316 cache->renderTargetFormat = rtformat;
317 cache->label =
label;
328 DestroyPipelineCache(METAL_PipelineCache *cache)
331 for (
int i = 0;
i < cache->count;
i++) {
332 CFBridgingRelease(cache->states[
i].pipe);
340 MakeShaderPipelines(METAL_RenderData *
data, METAL_ShaderPipelines *
pipelines, MTLPixelFormat rtformat)
344 pipelines->renderTargetFormat = rtformat;
346 MakePipelineCache(
data, &
pipelines->caches[SDL_METAL_FRAGMENT_SOLID],
"SDL primitives pipeline", rtformat, SDL_METAL_VERTEX_SOLID, SDL_METAL_FRAGMENT_SOLID);
347 MakePipelineCache(
data, &
pipelines->caches[SDL_METAL_FRAGMENT_COPY],
"SDL copy pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_COPY);
348 MakePipelineCache(
data, &
pipelines->caches[SDL_METAL_FRAGMENT_YUV],
"SDL YUV pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_YUV);
349 MakePipelineCache(
data, &
pipelines->caches[SDL_METAL_FRAGMENT_NV12],
"SDL NV12 pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_NV12);
350 MakePipelineCache(
data, &
pipelines->caches[SDL_METAL_FRAGMENT_NV21],
"SDL NV21 pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_NV21);
353 static METAL_ShaderPipelines *
354 ChooseShaderPipelines(METAL_RenderData *
data, MTLPixelFormat rtformat)
356 METAL_ShaderPipelines *allpipelines =
data.allpipelines;
360 if (allpipelines[
i].renderTargetFormat == rtformat) {
361 return &allpipelines[i];
365 allpipelines =
SDL_realloc(allpipelines, (
count + 1) *
sizeof(METAL_ShaderPipelines));
367 if (allpipelines ==
NULL) {
372 MakeShaderPipelines(
data, &allpipelines[
count], rtformat);
374 data.allpipelines = allpipelines;
377 return &
data.allpipelines[count];
381 DestroyAllPipelines(METAL_ShaderPipelines *allpipelines,
int count)
383 if (allpipelines !=
NULL) {
385 for (
int cache = 0; cache < SDL_METAL_FRAGMENT_COUNT; cache++) {
386 DestroyPipelineCache(&allpipelines[
i].caches[cache]);
395 ChoosePipelineState(METAL_RenderData *
data, METAL_ShaderPipelines *
pipelines, SDL_MetalFragmentFunction fragfn,
SDL_BlendMode blendmode)
397 METAL_PipelineCache *cache = &
pipelines->caches[fragfn];
399 for (
int i = 0;
i < cache->count;
i++) {
400 if (cache->states[
i].blendMode == blendmode) {
401 return (__bridge id<MTLRenderPipelineState>)cache->states[i].pipe;
405 return MakePipelineState(
data, cache, [NSString stringWithFormat:
@" (blend=custom 0x%x)", blendmode], blendmode);
409 METAL_ActivateRenderCommandEncoder(
SDL_Renderer *
renderer, MTLLoadAction load, MTLClearColor *clear_color)
415 if (
data.mtlcmdencoder == nil) {
420 mtltexture = texdata.mtltexture;
422 if (
data.mtlbackbuffer == nil) {
425 data.mtlbackbuffer = [data.mtllayer nextDrawable];
426 if (load == MTLLoadActionLoad) {
427 load = MTLLoadActionDontCare;
430 mtltexture =
data.mtlbackbuffer.texture;
435 if (load == MTLLoadActionClear) {
437 data.mtlpassdesc.colorAttachments[0].clearColor = *clear_color;
440 data.mtlpassdesc.colorAttachments[0].loadAction = load;
441 data.mtlpassdesc.colorAttachments[0].texture = mtltexture;
443 data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
444 data.mtlcmdencoder = [data.mtlcmdbuffer renderCommandEncoderWithDescriptor:data.mtlpassdesc];
446 if (
data.mtlbackbuffer != nil && mtltexture ==
data.mtlbackbuffer.texture) {
447 data.mtlcmdencoder.label =
@"SDL metal renderer backbuffer";
449 data.mtlcmdencoder.label =
@"SDL metal renderer render target";
452 data.activepipelines = ChooseShaderPipelines(
data, mtltexture.pixelFormat);
457 [data.mtlcmdbuffer enqueue];
475 *
w = (int)
data.mtllayer.drawableSize.width;
478 *
h = (int)
data.mtllayer.drawableSize.height;
493 if (GetBlendFactor(srcColorFactor) == invalidBlendFactor ||
494 GetBlendFactor(srcAlphaFactor) == invalidBlendFactor ||
495 GetBlendOperation(colorOperation) == invalidBlendOperation ||
496 GetBlendFactor(dstColorFactor) == invalidBlendFactor ||
497 GetBlendFactor(dstAlphaFactor) == invalidBlendFactor ||
498 GetBlendOperation(alphaOperation) == invalidBlendOperation) {
508 MTLPixelFormat pixfmt;
512 pixfmt = MTLPixelFormatRGBA8Unorm;
515 pixfmt = MTLPixelFormatBGRA8Unorm;
521 pixfmt = MTLPixelFormatR8Unorm;
527 MTLTextureDescriptor *mtltexdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:pixfmt
528 width:(NSUInteger)texture->w height:(NSUInteger)texture->h mipmapped:NO];
531 if ([mtltexdesc respondsToSelector:
@selector(
usage)]) {
533 mtltexdesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
535 mtltexdesc.usage = MTLTextureUsageShaderRead;
539 id<MTLTexture> mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
540 if (mtltexture == nil) {
550 mtltexdesc.pixelFormat = MTLPixelFormatR8Unorm;
551 mtltexdesc.width = (
texture->w + 1) / 2;
552 mtltexdesc.height = (
texture->h + 1) / 2;
553 mtltexdesc.textureType = MTLTextureType2DArray;
554 mtltexdesc.arrayLength = 2;
556 mtltexdesc.pixelFormat = MTLPixelFormatRG8Unorm;
557 mtltexdesc.width = (
texture->w + 1) / 2;
558 mtltexdesc.height = (
texture->h + 1) / 2;
562 mtltexture_uv = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
563 if (mtltexture_uv == nil) {
564 #if !__has_feature(objc_arc)
565 [mtltexture release];
571 METAL_TextureData *texturedata = [[METAL_TextureData alloc] init];
573 texturedata.mtlsampler =
data.mtlsamplernearest;
575 texturedata.mtlsampler =
data.mtlsamplerlinear;
577 texturedata.mtltexture = mtltexture;
578 texturedata.mtltexture_uv = mtltexture_uv;
580 texturedata.yuv = yuv;
581 texturedata.nv12 = nv12;
584 texturedata.fragmentFunction = SDL_METAL_FRAGMENT_YUV;
586 texturedata.fragmentFunction = SDL_METAL_FRAGMENT_NV12;
588 texturedata.fragmentFunction = SDL_METAL_FRAGMENT_NV21;
590 texturedata.fragmentFunction = SDL_METAL_FRAGMENT_COPY;
600 default:
offset = 0;
break;
602 texturedata.conversionBufferOffset =
offset;
605 texture->driverdata = (
void*)CFBridgingRetain(texturedata);
607 #if !__has_feature(objc_arc)
608 [texturedata release];
609 [mtltexture release];
610 [mtltexture_uv release];
618 const void *
pixels,
int pitch)
620 [texture replaceRegion:MTLRegionMake2D(rect.x, rect.y, rect.w, rect.h)
628 static MTLStorageMode
632 if ([resource respondsToSelector:
@selector(storageMode)]) {
633 return resource.storageMode;
635 return MTLStorageModeShared;
641 const void *
pixels,
int pitch)
645 MTLTextureDescriptor *desc;
650 if (!texturedata.hasdata && METAL_GetStorageMode(
texture) != MTLStorageModePrivate) {
655 desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:texture.pixelFormat
667 id<MTLTexture> stagingtex = [data.mtldevice newTextureWithDescriptor:desc];
668 if (stagingtex == nil) {
672 #if !__has_feature(objc_arc)
673 [stagingtex autorelease];
676 METAL_UploadTextureData(stagingtex, stagingrect, 0,
pixels, pitch);
678 if (
data.mtlcmdencoder != nil) {
679 [data.mtlcmdencoder endEncoding];
680 data.mtlcmdencoder = nil;
683 if (
data.mtlcmdbuffer == nil) {
684 data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
689 [blitcmd copyFromTexture:stagingtex
692 sourceOrigin:MTLOriginMake(0, 0, 0)
693 sourceSize:MTLSizeMake(rect.w, rect.h, 1)
695 destinationSlice:slice
697 destinationOrigin:MTLOriginMake(rect.x, rect.y, 0)];
699 [blitcmd endEncoding];
703 [data.mtlcmdbuffer commit];
704 data.mtlcmdbuffer = nil;
713 METAL_TextureData *texturedata = (__bridge METAL_TextureData *)
texture->driverdata;
715 if (METAL_UpdateTextureInternal(
renderer, texturedata, texturedata.mtltexture, *
rect, 0,
pixels, pitch) < 0) {
719 if (texturedata.yuv) {
722 int UVpitch = (pitch + 1) / 2;
727 if (METAL_UpdateTextureInternal(
renderer, texturedata, texturedata.mtltexture_uv, UVrect, Uslice,
pixels, UVpitch) < 0) {
733 if (METAL_UpdateTextureInternal(
renderer, texturedata, texturedata.mtltexture_uv, UVrect, Vslice,
pixels, UVpitch) < 0) {
738 if (texturedata.nv12) {
740 int UVpitch = 2 * ((pitch + 1) / 2);
744 if (METAL_UpdateTextureInternal(
renderer, texturedata, texturedata.mtltexture_uv, UVrect, 0,
pixels, UVpitch) < 0) {
749 texturedata.hasdata = YES;
757 const Uint8 *Yplane,
int Ypitch,
758 const Uint8 *Uplane,
int Upitch,
759 const Uint8 *Vplane,
int Vpitch)
761 METAL_TextureData *texturedata = (__bridge METAL_TextureData *)
texture->driverdata;
762 const int Uslice = 0;
763 const int Vslice = 1;
771 if (METAL_UpdateTextureInternal(
renderer, texturedata, texturedata.mtltexture, *
rect, 0, Yplane, Ypitch) < 0) {
774 if (METAL_UpdateTextureInternal(
renderer, texturedata, texturedata.mtltexture_uv, UVrect, Uslice, Uplane, Upitch)) {
777 if (METAL_UpdateTextureInternal(
renderer, texturedata, texturedata.mtltexture_uv, UVrect, Vslice, Vplane, Vpitch)) {
781 texturedata.hasdata = YES;
791 METAL_TextureData *texturedata = (__bridge METAL_TextureData *)
texture->driverdata;
795 return SDL_SetError(
"Invalid rectangle dimensions for LockTexture.");
800 if (texturedata.yuv || texturedata.nv12) {
801 buffersize = ((*pitch) *
rect->
h) + (2 * (*pitch + 1) / 2) * ((
rect->
h + 1) / 2);
803 buffersize = (*pitch) *
rect->
h;
806 texturedata.lockedrect = *
rect;
807 texturedata.lockedbuffer = [data.mtldevice newBufferWithLength:buffersize options:MTLResourceStorageModeShared];
808 if (texturedata.lockedbuffer == nil) {
812 *
pixels = [texturedata.lockedbuffer contents];
821 METAL_TextureData *texturedata = (__bridge METAL_TextureData *)
texture->driverdata;
826 if (texturedata.lockedbuffer == nil) {
830 if (
data.mtlcmdencoder != nil) {
831 [data.mtlcmdencoder endEncoding];
832 data.mtlcmdencoder = nil;
835 if (
data.mtlcmdbuffer == nil) {
836 data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
841 [blitcmd copyFromBuffer:texturedata.lockedbuffer
843 sourceBytesPerRow:pitch
844 sourceBytesPerImage:0
845 sourceSize:MTLSizeMake(rect.w, rect.h, 1)
846 toTexture:texturedata.mtltexture
849 destinationOrigin:MTLOriginMake(rect.x, rect.y, 0)];
851 if (texturedata.yuv) {
854 int UVpitch = (pitch + 1) / 2;
856 [blitcmd copyFromBuffer:texturedata.lockedbuffer
857 sourceOffset:rect.h * pitch
858 sourceBytesPerRow:UVpitch
859 sourceBytesPerImage:UVpitch * UVrect.h
860 sourceSize:MTLSizeMake(UVrect.w, UVrect.h, 1)
861 toTexture:texturedata.mtltexture_uv
862 destinationSlice:Uslice
864 destinationOrigin:MTLOriginMake(UVrect.x, UVrect.y, 0)];
866 [blitcmd copyFromBuffer:texturedata.lockedbuffer
867 sourceOffset:(rect.h * pitch) + UVrect.h * UVpitch
868 sourceBytesPerRow:UVpitch
869 sourceBytesPerImage:UVpitch * UVrect.h
870 sourceSize:MTLSizeMake(UVrect.w, UVrect.h, 1)
871 toTexture:texturedata.mtltexture_uv
872 destinationSlice:Vslice
874 destinationOrigin:MTLOriginMake(UVrect.x, UVrect.y, 0)];
877 if (texturedata.nv12) {
878 int UVpitch = 2 * ((pitch + 1) / 2);
880 [blitcmd copyFromBuffer:texturedata.lockedbuffer
881 sourceOffset:rect.h * pitch
882 sourceBytesPerRow:UVpitch
883 sourceBytesPerImage:0
884 sourceSize:MTLSizeMake(UVrect.w, UVrect.h, 1)
885 toTexture:texturedata.mtltexture_uv
888 destinationOrigin:MTLOriginMake(UVrect.x, UVrect.y, 0)];
891 [blitcmd endEncoding];
893 [data.mtlcmdbuffer commit];
894 data.mtlcmdbuffer = nil;
896 #if !__has_feature(objc_arc)
897 [texturedata.lockedbuffer release];
900 texturedata.lockedbuffer = nil;
901 texturedata.hasdata = YES;
909 if (
data.mtlcmdencoder) {
912 [data.mtlcmdencoder endEncoding];
913 [data.mtlcmdbuffer commit];
915 data.mtlcmdencoder = nil;
916 data.mtlcmdbuffer = nil;
928 normtex(
const float _val,
const float len)
936 float projection[4][4];
939 const size_t matrixlen =
sizeof (projection);
947 projection[0][0] = 2.0f /
w;
948 projection[1][1] = -2.0
f /
h;
949 projection[3][0] = -1.0
f;
950 projection[3][1] = 1.0f;
951 projection[3][3] = 1.0f;
961 const size_t vertlen =
sizeof (float) * 4;
976 const size_t vertlen = (
sizeof (float) * 2) *
count;
989 const size_t vertlen = (
sizeof (float) * 8) *
count;
1003 if ((
rects->w <= 0.0f) || (
rects->h <= 0.0f)) {
1028 const float texw = (float)
texture->w;
1029 const float texh = (
float)
texture->h;
1031 const size_t vertlen = (
sizeof (float) * 16);
1039 *(verts++) = dstrect->
x;
1040 *(verts++) = dstrect->
y + dstrect->
h;
1041 *(verts++) = dstrect->
x;
1042 *(verts++) = dstrect->
y;
1043 *(verts++) = dstrect->
x + dstrect->
w;
1044 *(verts++) = dstrect->
y + dstrect->
h;
1045 *(verts++) = dstrect->
x + dstrect->
w;
1046 *(verts++) = dstrect->
y;
1048 *(verts++) = normtex(srcrect->
x, texw);
1049 *(verts++) = normtex(srcrect->
y + srcrect->
h, texh);
1050 *(verts++) = normtex(srcrect->
x, texw);
1051 *(verts++) = normtex(srcrect->
y, texh);
1052 *(verts++) = normtex(srcrect->
x + srcrect->
w, texw);
1053 *(verts++) = normtex(srcrect->
y + srcrect->
h, texh);
1054 *(verts++) = normtex(srcrect->
x + srcrect->
w, texw);
1055 *(verts++) = normtex(srcrect->
y, texh);
1065 const float texw = (float)
texture->
w;
1066 const float texh = (
float)
texture->h;
1067 const float rads = (float)(M_PI * (
float)
angle / 180.0f);
1068 const float c = cosf(rads),
s = sinf(rads);
1069 float minu, maxu, minv, maxv;
1070 const size_t vertlen = (
sizeof (float) * 32);
1080 SDL_memset(verts,
'\0',
sizeof (*verts) * 16);
1081 verts[10] = verts[15] = 1.0f;
1089 verts[12] = dstrect->
x + center->
x;
1090 verts[13] = dstrect->
y + center->
y;
1098 minu = normtex(srcquad->
x, texw);
1099 maxu = normtex(srcquad->
x + srcquad->
w, texw);
1100 minv = normtex(srcquad->
y, texh);
1101 maxv = normtex(srcquad->
y + srcquad->
h, texh);
1115 *(verts++) = -center->
x;
1116 *(verts++) = dstrect->
h - center->
y;
1117 *(verts++) = -center->
x;
1118 *(verts++) = -center->
y;
1119 *(verts++) = dstrect->
w - center->
x;
1120 *(verts++) = dstrect->
h - center->
y;
1121 *(verts++) = dstrect->
w - center->
x;
1122 *(verts++) = -center->
y;
1140 #if __has_feature(objc_arc)
1145 size_t constants_offset;
1152 size_t projection_offset;
1154 size_t color_offset;
1155 } METAL_DrawStateCache;
1159 const size_t constants_offset,
id<MTLBuffer> mtlbufvertex, METAL_DrawStateCache *statecache)
1166 METAL_ActivateRenderCommandEncoder(
renderer, MTLLoadActionLoad,
NULL);
1168 if (statecache->viewport_dirty) {
1170 viewport.originX = statecache->viewport.
x;
1171 viewport.originY = statecache->viewport.
y;
1172 viewport.width = statecache->viewport.
w;
1173 viewport.height = statecache->viewport.
h;
1176 [data.mtlcmdencoder setViewport:viewport];
1177 [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:statecache->projection_offset atIndex:2];
1181 if (statecache->cliprect_dirty) {
1182 MTLScissorRect mtlrect;
1183 if (statecache->cliprect_enabled) {
1185 mtlrect.
x = statecache->viewport.x +
rect->
x;
1186 mtlrect.
y = statecache->viewport.y +
rect->
y;
1187 mtlrect.width =
rect->
w;
1188 mtlrect.height =
rect->
h;
1190 mtlrect.x = statecache->viewport.x;
1191 mtlrect.y = statecache->viewport.y;
1192 mtlrect.width = statecache->viewport.w;
1193 mtlrect.height = statecache->viewport.h;
1195 if (mtlrect.width > 0 && mtlrect.height > 0) {
1196 [data.mtlcmdencoder setScissorRect:mtlrect];
1201 if (statecache->color_dirty) {
1202 [data.mtlcmdencoder setFragmentBuffer:mtlbufvertex offset:statecache->color_offset atIndex:0];
1206 newpipeline = ChoosePipelineState(
data,
data.activepipelines,
shader, blend);
1207 if (newpipeline != statecache->pipeline) {
1208 [data.mtlcmdencoder setRenderPipelineState:newpipeline];
1209 statecache->pipeline = newpipeline;
1212 if (constants_offset != statecache->constants_offset) {
1213 if (constants_offset != CONSTANTS_OFFSET_INVALID) {
1214 [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:constants_offset atIndex:3];
1216 statecache->constants_offset = constants_offset;
1219 [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:first atIndex:0];
1224 id<MTLBuffer> mtlbufvertex, METAL_DrawStateCache *statecache)
1228 METAL_TextureData *texturedata = (__bridge METAL_TextureData *)
texture->driverdata;
1230 SetDrawState(
renderer, cmd, texturedata.fragmentFunction, constants_offset, mtlbufvertex, statecache);
1232 [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:cmd->data.draw.first+(8*sizeof (float)) atIndex:1];
1234 if (
texture != statecache->texture) {
1235 METAL_TextureData *oldtexturedata =
NULL;
1236 if (statecache->texture) {
1237 oldtexturedata = (__bridge METAL_TextureData *) statecache->texture->driverdata;
1239 if (!oldtexturedata || (texturedata.mtlsampler != oldtexturedata.mtlsampler)) {
1240 [data.mtlcmdencoder setFragmentSamplerState:texturedata.mtlsampler atIndex:0];
1243 [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0];
1244 if (texturedata.yuv || texturedata.nv12) {
1245 [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture_uv atIndex:1];
1246 [data.mtlcmdencoder setFragmentBuffer:data.mtlbufconstants offset:texturedata.conversionBufferOffset atIndex:1];
1248 statecache->texture =
texture;
1254 { @autoreleasepool {
1256 METAL_DrawStateCache statecache;
1259 statecache.pipeline = nil;
1260 statecache.constants_offset = CONSTANTS_OFFSET_INVALID;
1261 statecache.texture =
NULL;
1263 statecache.cliprect_dirty =
SDL_TRUE;
1264 statecache.viewport_dirty =
SDL_TRUE;
1265 statecache.projection_offset = 0;
1266 statecache.color_offset = 0;
1277 mtlbufvertex = [data.mtldevice newBufferWithLength:vertsize options:MTLResourceStorageModeShared];
1278 #if !__has_feature(objc_arc)
1279 [mtlbufvertex autorelease];
1281 mtlbufvertex.label =
@"SDL vertex data";
1282 SDL_memcpy([mtlbufvertex contents], vertices, vertsize);
1286 [data.mtlcmdencoder endEncoding];
1287 [data.mtlcmdbuffer commit];
1288 data.mtlcmdencoder = nil;
1289 data.mtlcmdbuffer = nil;
1295 statecache.projection_offset = cmd->
data.
viewport.first;
1296 statecache.viewport_dirty =
SDL_TRUE;
1302 statecache.cliprect_enabled = cmd->
data.
cliprect.enabled;
1303 statecache.cliprect_dirty =
SDL_TRUE;
1308 statecache.color_offset = cmd->
data.
color.first;
1317 if (
data.mtlcmdencoder != nil) {
1318 [data.mtlcmdencoder endEncoding];
1321 [data.mtlcmdbuffer commit];
1322 data.mtlcmdencoder = nil;
1323 data.mtlcmdbuffer = nil;
1327 statecache.pipeline = nil;
1328 statecache.constants_offset = CONSTANTS_OFFSET_INVALID;
1329 statecache.texture =
NULL;
1331 statecache.cliprect_dirty =
SDL_TRUE;
1332 statecache.viewport_dirty =
SDL_TRUE;
1338 MTLClearColor
color = MTLClearColorMake(
r / 255.0
f,
g / 255.0
f,
b / 255.0
f,
a / 255.0
f);
1341 METAL_ActivateRenderCommandEncoder(
renderer, MTLLoadActionClear, &
color);
1349 SetDrawState(
renderer, cmd, SDL_METAL_FRAGMENT_SOLID, CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM, mtlbufvertex, &statecache);
1350 [data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];
1356 const size_t maxcount = UINT16_MAX / 4;
1357 SetDrawState(
renderer, cmd, SDL_METAL_FRAGMENT_SOLID, CONSTANTS_OFFSET_IDENTITY, mtlbufvertex, &statecache);
1360 for (
size_t i = 0;
i <
count;
i += maxcount) {
1363 [data.mtlcmdencoder setVertexBufferOffset:cmd->data.draw.first + i*sizeof(float)*8 atIndex:0];
1364 [data.mtlcmdencoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle
1365 indexCount:SDL_min(maxcount, count - i) * 6
1366 indexType:MTLIndexTypeUInt16
1367 indexBuffer:data.mtlbufquadindices
1368 indexBufferOffset:0];
1374 SetCopyState(
renderer, cmd, CONSTANTS_OFFSET_IDENTITY, mtlbufvertex, &statecache);
1375 [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
1380 SetCopyState(
renderer, cmd, CONSTANTS_OFFSET_INVALID, mtlbufvertex, &statecache);
1381 [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:cmd->data.draw.count atIndex:3];
1382 [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
1398 { @autoreleasepool {
1400 METAL_ActivateRenderCommandEncoder(
renderer, MTLLoadActionLoad,
NULL);
1402 [data.mtlcmdencoder endEncoding];
1410 if (METAL_GetStorageMode(mtltexture) == MTLStorageModeManaged) {
1412 [blit synchronizeResource:mtltexture];
1419 [data.mtlcmdbuffer commit];
1420 [data.mtlcmdbuffer waitUntilCompleted];
1421 data.mtlcmdencoder = nil;
1422 data.mtlcmdbuffer = nil;
1427 const int temp_pitch =
rect->
w * 4;
1433 [mtltexture getBytes:temp_pixels bytesPerRow:temp_pitch fromRegion:mtlregion mipmapLevel:0];
1443 { @autoreleasepool {
1446 if (
data.mtlcmdencoder != nil) {
1447 [data.mtlcmdencoder endEncoding];
1449 if (
data.mtlbackbuffer != nil) {
1450 [data.mtlcmdbuffer presentDrawable:data.mtlbackbuffer];
1452 if (
data.mtlcmdbuffer != nil) {
1453 [data.mtlcmdbuffer commit];
1455 data.mtlcmdencoder = nil;
1456 data.mtlcmdbuffer = nil;
1457 data.mtlbackbuffer = nil;
1462 { @autoreleasepool {
1463 CFBridgingRelease(
texture->driverdata);
1469 { @autoreleasepool {
1473 if (
data.mtlcmdencoder != nil) {
1474 [data.mtlcmdencoder endEncoding];
1477 DestroyAllPipelines(
data.allpipelines,
data.pipelinescount);
1485 { @autoreleasepool {
1487 return (__bridge
void*)
data.mtllayer;
1492 { @autoreleasepool {
1493 METAL_ActivateRenderCommandEncoder(
renderer, MTLLoadActionLoad,
NULL);
1495 return (__bridge
void*)
data.mtlcmdencoder;
1500 { @autoreleasepool {
1511 if (IsMetalAvailable(&syswm) == -1) {
1522 mtldevice = MTLCreateSystemDefaultDevice();
1524 if (mtldevice == nil) {
1531 data = [[METAL_RenderData alloc] init];
1537 NSView *view = Cocoa_Mtl_AddMetalView(
window);
1538 CAMetalLayer *
layer = (CAMetalLayer *)[view
layer];
1540 layer.device = mtldevice;
1545 UIView *view = UIKit_Mtl_AddMetalView(
window);
1546 CAMetalLayer *
layer = (CAMetalLayer *)[view
layer];
1550 layer.framebufferOnly = NO;
1555 data.mtlcmdqueue = mtlcmdqueue;
1556 data.mtlcmdqueue.label =
@"SDL Metal Renderer";
1557 data.mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor];
1564 id<MTLLibrary> mtllibrary = [data.mtldevice newLibraryWithData:mtllibdata error:&err];
1565 data.mtllibrary = mtllibrary;
1567 #if !__has_feature(objc_arc)
1568 dispatch_release(mtllibdata);
1570 data.mtllibrary.label =
@"SDL Metal renderer shader library";
1573 data.pipelinescount = 0;
1575 ChooseShaderPipelines(
data, MTLPixelFormatBGRA8Unorm);
1577 MTLSamplerDescriptor *samplerdesc = [[MTLSamplerDescriptor alloc] init];
1579 samplerdesc.minFilter = MTLSamplerMinMagFilterNearest;
1580 samplerdesc.magFilter = MTLSamplerMinMagFilterNearest;
1581 id<MTLSamplerState> mtlsamplernearest = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc];
1582 data.mtlsamplernearest = mtlsamplernearest;
1584 samplerdesc.minFilter = MTLSamplerMinMagFilterLinear;
1585 samplerdesc.magFilter = MTLSamplerMinMagFilterLinear;
1586 id<MTLSamplerState> mtlsamplerlinear = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc];
1587 data.mtlsamplerlinear = mtlsamplerlinear;
1590 float identitytransform[16] = {
1591 1.0f, 0.0f, 0.0f, 0.0f,
1592 0.0f, 1.0f, 0.0f, 0.0f,
1593 0.0f, 0.0f, 1.0f, 0.0f,
1594 0.0f, 0.0f, 0.0f, 1.0f,
1597 float halfpixeltransform[16] = {
1598 1.0f, 0.0f, 0.0f, 0.0f,
1599 0.0f, 1.0f, 0.0f, 0.0f,
1600 0.0f, 0.0f, 1.0f, 0.0f,
1601 0.5f, 0.5f, 0.0f, 1.0f,
1605 float decodetransformJPEG[4*4] = {
1606 0.0, -0.501960814, -0.501960814, 0.0,
1607 1.0000, 0.0000, 1.4020, 0.0,
1608 1.0000, -0.3441, -0.7141, 0.0,
1609 1.0000, 1.7720, 0.0000, 0.0,
1612 float decodetransformBT601[4*4] = {
1613 -0.0627451017, -0.501960814, -0.501960814, 0.0,
1614 1.1644, 0.0000, 1.5960, 0.0,
1615 1.1644, -0.3918, -0.8130, 0.0,
1616 1.1644, 2.0172, 0.0000, 0.0,
1619 float decodetransformBT709[4*4] = {
1620 0.0, -0.501960814, -0.501960814, 0.0,
1621 1.0000, 0.0000, 1.4020, 0.0,
1622 1.0000, -0.3441, -0.7141, 0.0,
1623 1.0000, 1.7720, 0.0000, 0.0,
1626 id<MTLBuffer> mtlbufconstantstaging = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModeShared];
1627 #if !__has_feature(objc_arc)
1628 [mtlbufconstantstaging autorelease];
1631 char *constantdata = [mtlbufconstantstaging contents];
1632 SDL_memcpy(constantdata + CONSTANTS_OFFSET_IDENTITY, identitytransform,
sizeof(identitytransform));
1633 SDL_memcpy(constantdata + CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM, halfpixeltransform,
sizeof(halfpixeltransform));
1634 SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_JPEG, decodetransformJPEG,
sizeof(decodetransformJPEG));
1635 SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT601, decodetransformBT601,
sizeof(decodetransformBT601));
1636 SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT709, decodetransformBT709,
sizeof(decodetransformBT709));
1638 int quadcount = UINT16_MAX / 4;
1639 size_t indicessize =
sizeof(UInt16) * quadcount * 6;
1640 id<MTLBuffer> mtlbufquadindicesstaging = [data.mtldevice newBufferWithLength:indicessize options:MTLResourceStorageModeShared];
1641 #if !__has_feature(objc_arc)
1642 [mtlbufquadindicesstaging autorelease];
1650 UInt16 *indexdata = [mtlbufquadindicesstaging contents];
1651 for (
int i = 0;
i < quadcount;
i++) {
1652 indexdata[i * 6 + 0] =
i * 4 + 0;
1653 indexdata[i * 6 + 1] =
i * 4 + 1;
1654 indexdata[i * 6 + 2] =
i * 4 + 2;
1656 indexdata[i * 6 + 3] =
i * 4 + 2;
1657 indexdata[i * 6 + 4] =
i * 4 + 1;
1658 indexdata[i * 6 + 5] =
i * 4 + 3;
1661 id<MTLBuffer> mtlbufconstants = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModePrivate];
1662 data.mtlbufconstants = mtlbufconstants;
1663 data.mtlbufconstants.label =
@"SDL constant data";
1665 id<MTLBuffer> mtlbufquadindices = [data.mtldevice newBufferWithLength:indicessize options:MTLResourceStorageModePrivate];
1666 data.mtlbufquadindices = mtlbufquadindices;
1667 data.mtlbufquadindices.label =
@"SDL quad index buffer";
1672 [blitcmd copyFromBuffer:mtlbufconstantstaging sourceOffset:0 toBuffer:mtlbufconstants destinationOffset:0 size:CONSTANTS_LENGTH];
1673 [blitcmd copyFromBuffer:mtlbufquadindicesstaging sourceOffset:0 toBuffer:mtlbufquadindices destinationOffset:0 size:indicessize];
1675 [blitcmd endEncoding];
1709 #if defined(__MACOSX__) && defined(MAC_OS_X_VERSION_10_13)
1712 if (
data.mtllayer.displaySyncEnabled) {
1722 int maxtexsize = 4096;
1723 #if defined(__MACOSX__)
1725 #elif defined(__TVOS__)
1729 if ([mtldevice supportsFeatureSet:MTLFeatureSet_tvOS_GPUFamily2_v1]) {
1735 #ifdef __IPHONE_11_0
1736 #pragma clang diagnostic push
1737 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
1738 if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily4_v1]) {
1741 #pragma clang diagnostic pop
1743 #ifdef __IPHONE_10_0
1744 if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
1748 if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v2] || [mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v2]) {
1758 #if !__has_feature(objc_arc)
1759 [mtlcmdqueue release];
1760 [mtllibrary release];
1761 [samplerdesc release];
1762 [mtlsamplernearest release];
1763 [mtlsamplerlinear release];
1764 [mtlbufconstants release];
1765 [mtlbufquadindices release];
1768 [mtldevice release];
1775 METAL_CreateRenderer,