21 #include "../../SDL_internal.h"
23 #if SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED
30 #include "../SDL_sysrender.h"
32 #include <Availability.h>
33 #import <Metal/Metal.h>
34 #import <QuartzCore/CAMetalLayer.h>
37 #import <AppKit/NSView.h>
42 #include "SDL_shaders_metal_osx.h"
43 #elif defined(__TVOS__)
44 #include "SDL_shaders_metal_tvos.h"
46 #include "SDL_shaders_metal_ios.h"
54 #define CONSTANT_ALIGN(x) (256)
56 #define CONSTANT_ALIGN(x) (x < 4 ? 4 : x)
59 #define DEVICE_ALIGN(x) (x < 4 ? 4 : x)
61 #define ALIGN_CONSTANTS(align, size) ((size + CONSTANT_ALIGN(align) - 1) & (~(CONSTANT_ALIGN(align) - 1)))
63 static const size_t CONSTANTS_OFFSET_INVALID = 0xFFFFFFFF;
64 static const size_t CONSTANTS_OFFSET_IDENTITY = 0;
65 static const size_t CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM = ALIGN_CONSTANTS(16, CONSTANTS_OFFSET_IDENTITY +
sizeof(
float) * 16);
66 static const size_t CONSTANTS_OFFSET_DECODE_JPEG = ALIGN_CONSTANTS(16, CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM +
sizeof(
float) * 16);
67 static const size_t CONSTANTS_OFFSET_DECODE_BT601 = ALIGN_CONSTANTS(16, CONSTANTS_OFFSET_DECODE_JPEG +
sizeof(
float) * 4 * 4);
68 static const size_t CONSTANTS_OFFSET_DECODE_BT709 = ALIGN_CONSTANTS(16, CONSTANTS_OFFSET_DECODE_BT601 +
sizeof(
float) * 4 * 4);
69 static const size_t CONSTANTS_LENGTH = CONSTANTS_OFFSET_DECODE_BT709 + sizeof(float) * 4 * 4;
71 typedef enum SDL_MetalVertexFunction
73 SDL_METAL_VERTEX_SOLID,
74 SDL_METAL_VERTEX_COPY,
75 } SDL_MetalVertexFunction;
77 typedef enum SDL_MetalFragmentFunction
79 SDL_METAL_FRAGMENT_SOLID = 0,
80 SDL_METAL_FRAGMENT_COPY,
81 SDL_METAL_FRAGMENT_YUV,
82 SDL_METAL_FRAGMENT_NV12,
83 SDL_METAL_FRAGMENT_NV21,
84 SDL_METAL_FRAGMENT_COUNT,
85 } SDL_MetalFragmentFunction;
87 typedef struct METAL_PipelineState
91 } METAL_PipelineState;
93 typedef struct METAL_PipelineCache
95 METAL_PipelineState *states;
97 SDL_MetalVertexFunction vertexFunction;
98 SDL_MetalFragmentFunction fragmentFunction;
99 MTLPixelFormat renderTargetFormat;
101 } METAL_PipelineCache;
109 typedef struct METAL_ShaderPipelines
111 MTLPixelFormat renderTargetFormat;
112 METAL_PipelineCache caches[SDL_METAL_FRAGMENT_COUNT];
113 } METAL_ShaderPipelines;
115 @interface METAL_RenderData : NSObject
125 @property (nonatomic, retain)
id<MTLBuffer> mtlbufquadindices;
127 @property (nonatomic, retain) CAMetalLayer *mtllayer;
128 @property (nonatomic, retain) MTLRenderPassDescriptor *mtlpassdesc;
129 @property (nonatomic, assign) METAL_ShaderPipelines *activepipelines;
130 @property (nonatomic, assign) METAL_ShaderPipelines *allpipelines;
131 @property (nonatomic, assign)
int pipelinescount;
134 @implementation METAL_RenderData
135 #if !__has_feature(objc_arc)
138 [_mtldevice release];
139 [_mtlcmdqueue release];
140 [_mtlcmdbuffer release];
141 [_mtlcmdencoder release];
142 [_mtllibrary release];
143 [_mtlbackbuffer release];
144 [_mtlsamplernearest release];
145 [_mtlsamplerlinear release];
146 [_mtlbufconstants release];
147 [_mtlbufquadindices release];
149 [_mtlpassdesc release];
155 @interface METAL_TextureData : NSObject
159 @property (nonatomic, assign) SDL_MetalFragmentFunction fragmentFunction;
160 @property (nonatomic, assign) BOOL yuv;
161 @property (nonatomic, assign) BOOL nv12;
162 @property (nonatomic, assign)
size_t conversionBufferOffset;
163 @property (nonatomic, assign) BOOL hasdata;
166 @property (nonatomic, assign)
SDL_Rect lockedrect;
169 @implementation METAL_TextureData
170 #if !__has_feature(objc_arc)
173 [_mtltexture release];
174 [_mtltexture_uv release];
175 [_mtlsampler release];
176 [_lockedbuffer release];
186 return SDL_SetError(
"Metal render target only supports Cocoa and UIKit video targets at the moment.");
190 #if (defined(__MACOSX__) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101100))
191 if (MTLCreateSystemDefaultDevice ==
NULL) {
192 return SDL_SetError(
"Metal framework not available on this system");
199 static const MTLBlendOperation invalidBlendOperation = (MTLBlendOperation)0xFFFFFFFF;
200 static const MTLBlendFactor invalidBlendFactor = (MTLBlendFactor)0xFFFFFFFF;
202 static MTLBlendOperation
211 default:
return invalidBlendOperation;
215 static MTLBlendFactor
229 default:
return invalidBlendFactor;
234 GetVertexFunctionName(SDL_MetalVertexFunction
function)
237 case SDL_METAL_VERTEX_SOLID:
return @"SDL_Solid_vertex";
238 case SDL_METAL_VERTEX_COPY:
return @"SDL_Copy_vertex";
244 GetFragmentFunctionName(SDL_MetalFragmentFunction
function)
247 case SDL_METAL_FRAGMENT_SOLID:
return @"SDL_Solid_fragment";
248 case SDL_METAL_FRAGMENT_COPY:
return @"SDL_Copy_fragment";
249 case SDL_METAL_FRAGMENT_YUV:
return @"SDL_YUV_fragment";
250 case SDL_METAL_FRAGMENT_NV12:
return @"SDL_NV12_fragment";
251 case SDL_METAL_FRAGMENT_NV21:
return @"SDL_NV21_fragment";
257 MakePipelineState(METAL_RenderData *
data, METAL_PipelineCache *cache,
260 id<MTLFunction> mtlvertfn = [data.mtllibrary newFunctionWithName:GetVertexFunctionName(cache->vertexFunction)];
261 id<MTLFunction> mtlfragfn = [data.mtllibrary newFunctionWithName:GetFragmentFunctionName(cache->fragmentFunction)];
265 MTLRenderPipelineDescriptor *mtlpipedesc = [[MTLRenderPipelineDescriptor alloc] init];
266 mtlpipedesc.vertexFunction = mtlvertfn;
267 mtlpipedesc.fragmentFunction = mtlfragfn;
269 MTLVertexDescriptor *vertdesc = [MTLVertexDescriptor vertexDescriptor];
271 switch (cache->vertexFunction) {
272 case SDL_METAL_VERTEX_SOLID:
274 vertdesc.layouts[0].stride =
sizeof(float) * 2;
275 vertdesc.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
277 vertdesc.attributes[0].format = MTLVertexFormatFloat2;
278 vertdesc.attributes[0].offset = 0;
279 vertdesc.attributes[0].bufferIndex = 0;
281 case SDL_METAL_VERTEX_COPY:
283 vertdesc.layouts[0].stride =
sizeof(float) * 4;
284 vertdesc.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
286 vertdesc.attributes[0].format = MTLVertexFormatFloat2;
287 vertdesc.attributes[0].offset = 0;
288 vertdesc.attributes[0].bufferIndex = 0;
290 vertdesc.attributes[1].format = MTLVertexFormatFloat2;
291 vertdesc.attributes[1].offset =
sizeof(float) * 2;
292 vertdesc.attributes[1].bufferIndex = 0;
296 mtlpipedesc.vertexDescriptor = vertdesc;
298 MTLRenderPipelineColorAttachmentDescriptor *rtdesc = mtlpipedesc.colorAttachments[0];
299 rtdesc.pixelFormat = cache->renderTargetFormat;
302 rtdesc.blendingEnabled = YES;
310 rtdesc.blendingEnabled = NO;
313 mtlpipedesc.label = [@(cache->label) stringByAppendingString:blendlabel];
319 METAL_PipelineState pipeline;
320 pipeline.blendMode = blendmode;
321 pipeline.pipe = (
void *)CFBridgingRetain(
state);
323 METAL_PipelineState *states =
SDL_realloc(cache->states, (cache->count + 1) *
sizeof(pipeline));
325 #if !__has_feature(objc_arc)
326 [mtlpipedesc release];
333 states[cache->count++] = pipeline;
334 cache->states = states;
337 CFBridgingRelease(pipeline.pipe);
344 MakePipelineCache(METAL_RenderData *
data, METAL_PipelineCache *cache,
const char *
label,
345 MTLPixelFormat rtformat, SDL_MetalVertexFunction vertfn, SDL_MetalFragmentFunction fragfn)
349 cache->vertexFunction = vertfn;
350 cache->fragmentFunction = fragfn;
351 cache->renderTargetFormat = rtformat;
352 cache->label =
label;
364 DestroyPipelineCache(METAL_PipelineCache *cache)
367 for (
int i = 0;
i < cache->count;
i++) {
368 CFBridgingRelease(cache->states[
i].pipe);
376 MakeShaderPipelines(METAL_RenderData *
data, METAL_ShaderPipelines *
pipelines, MTLPixelFormat rtformat)
380 pipelines->renderTargetFormat = rtformat;
382 MakePipelineCache(
data, &
pipelines->caches[SDL_METAL_FRAGMENT_SOLID],
"SDL primitives pipeline", rtformat, SDL_METAL_VERTEX_SOLID, SDL_METAL_FRAGMENT_SOLID);
383 MakePipelineCache(
data, &
pipelines->caches[SDL_METAL_FRAGMENT_COPY],
"SDL copy pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_COPY);
384 MakePipelineCache(
data, &
pipelines->caches[SDL_METAL_FRAGMENT_YUV],
"SDL YUV pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_YUV);
385 MakePipelineCache(
data, &
pipelines->caches[SDL_METAL_FRAGMENT_NV12],
"SDL NV12 pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_NV12);
386 MakePipelineCache(
data, &
pipelines->caches[SDL_METAL_FRAGMENT_NV21],
"SDL NV21 pipeline", rtformat, SDL_METAL_VERTEX_COPY, SDL_METAL_FRAGMENT_NV21);
389 static METAL_ShaderPipelines *
390 ChooseShaderPipelines(METAL_RenderData *
data, MTLPixelFormat rtformat)
392 METAL_ShaderPipelines *allpipelines =
data.allpipelines;
396 if (allpipelines[
i].renderTargetFormat == rtformat) {
397 return &allpipelines[i];
401 allpipelines =
SDL_realloc(allpipelines, (
count + 1) *
sizeof(METAL_ShaderPipelines));
403 if (allpipelines ==
NULL) {
408 MakeShaderPipelines(
data, &allpipelines[
count], rtformat);
410 data.allpipelines = allpipelines;
413 return &
data.allpipelines[count];
417 DestroyAllPipelines(METAL_ShaderPipelines *allpipelines,
int count)
419 if (allpipelines !=
NULL) {
421 for (
int cache = 0; cache < SDL_METAL_FRAGMENT_COUNT; cache++) {
422 DestroyPipelineCache(&allpipelines[
i].caches[cache]);
431 ChoosePipelineState(METAL_RenderData *
data, METAL_ShaderPipelines *
pipelines, SDL_MetalFragmentFunction fragfn,
SDL_BlendMode blendmode)
433 METAL_PipelineCache *cache = &
pipelines->caches[fragfn];
435 for (
int i = 0;
i < cache->count;
i++) {
436 if (cache->states[
i].blendMode == blendmode) {
437 return (__bridge id<MTLRenderPipelineState>)cache->states[i].pipe;
441 return MakePipelineState(
data, cache, [NSString stringWithFormat:
@" (blend=custom 0x%x)", blendmode], blendmode);
451 if (
data.mtlcmdencoder == nil) {
456 mtltexture = texdata.mtltexture;
458 if (
data.mtlbackbuffer == nil) {
461 data.mtlbackbuffer = [data.mtllayer nextDrawable];
462 if (load == MTLLoadActionLoad) {
463 load = MTLLoadActionDontCare;
466 mtltexture =
data.mtlbackbuffer.texture;
471 if (load == MTLLoadActionClear) {
473 data.mtlpassdesc.colorAttachments[0].clearColor = *clear_color;
476 data.mtlpassdesc.colorAttachments[0].loadAction = load;
477 data.mtlpassdesc.colorAttachments[0].texture = mtltexture;
479 data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
480 data.mtlcmdencoder = [data.mtlcmdbuffer renderCommandEncoderWithDescriptor:data.mtlpassdesc];
482 if (
data.mtlbackbuffer != nil && mtltexture ==
data.mtlbackbuffer.texture) {
483 data.mtlcmdencoder.label =
@"SDL metal renderer backbuffer";
485 data.mtlcmdencoder.label =
@"SDL metal renderer render target";
490 if (vertex_buffer != nil) {
491 [data.mtlcmdencoder setVertexBuffer:vertex_buffer offset:0 atIndex:0];
492 [data.mtlcmdencoder setFragmentBuffer:vertex_buffer offset:0 atIndex:0];
495 data.activepipelines = ChooseShaderPipelines(
data, mtltexture.pixelFormat);
500 [data.mtlcmdbuffer enqueue];
518 *
w = (int)
data.mtllayer.drawableSize.width;
521 *
h = (int)
data.mtllayer.drawableSize.height;
536 if (GetBlendFactor(srcColorFactor) == invalidBlendFactor ||
537 GetBlendFactor(srcAlphaFactor) == invalidBlendFactor ||
538 GetBlendOperation(colorOperation) == invalidBlendOperation ||
539 GetBlendFactor(dstColorFactor) == invalidBlendFactor ||
540 GetBlendFactor(dstAlphaFactor) == invalidBlendFactor ||
541 GetBlendOperation(alphaOperation) == invalidBlendOperation) {
551 MTLPixelFormat pixfmt;
555 pixfmt = MTLPixelFormatRGBA8Unorm;
558 pixfmt = MTLPixelFormatBGRA8Unorm;
564 pixfmt = MTLPixelFormatR8Unorm;
570 MTLTextureDescriptor *mtltexdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:pixfmt
571 width:(NSUInteger)texture->w height:(NSUInteger)texture->h mipmapped:NO];
574 if ([mtltexdesc respondsToSelector:
@selector(
usage)]) {
576 mtltexdesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
578 mtltexdesc.usage = MTLTextureUsageShaderRead;
582 id<MTLTexture> mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
583 if (mtltexture == nil) {
593 mtltexdesc.pixelFormat = MTLPixelFormatR8Unorm;
594 mtltexdesc.width = (
texture->w + 1) / 2;
595 mtltexdesc.height = (
texture->h + 1) / 2;
596 mtltexdesc.textureType = MTLTextureType2DArray;
597 mtltexdesc.arrayLength = 2;
599 mtltexdesc.pixelFormat = MTLPixelFormatRG8Unorm;
600 mtltexdesc.width = (
texture->w + 1) / 2;
601 mtltexdesc.height = (
texture->h + 1) / 2;
605 mtltexture_uv = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
606 if (mtltexture_uv == nil) {
607 #if !__has_feature(objc_arc)
608 [mtltexture release];
614 METAL_TextureData *texturedata = [[METAL_TextureData alloc] init];
616 texturedata.mtlsampler =
data.mtlsamplernearest;
618 texturedata.mtlsampler =
data.mtlsamplerlinear;
620 texturedata.mtltexture = mtltexture;
621 texturedata.mtltexture_uv = mtltexture_uv;
623 texturedata.yuv = yuv;
624 texturedata.nv12 = nv12;
627 texturedata.fragmentFunction = SDL_METAL_FRAGMENT_YUV;
629 texturedata.fragmentFunction = SDL_METAL_FRAGMENT_NV12;
631 texturedata.fragmentFunction = SDL_METAL_FRAGMENT_NV21;
633 texturedata.fragmentFunction = SDL_METAL_FRAGMENT_COPY;
643 default:
offset = 0;
break;
645 texturedata.conversionBufferOffset =
offset;
648 texture->driverdata = (
void*)CFBridgingRetain(texturedata);
650 #if !__has_feature(objc_arc)
651 [texturedata release];
652 [mtltexture release];
653 [mtltexture_uv release];
661 const void *
pixels,
int pitch)
663 [texture replaceRegion:MTLRegionMake2D(rect.x, rect.y, rect.w, rect.h)
671 static MTLStorageMode
675 if ([resource respondsToSelector:
@selector(storageMode)]) {
676 return resource.storageMode;
678 return MTLStorageModeShared;
684 const void *
pixels,
int pitch)
688 MTLTextureDescriptor *desc;
693 if (!texturedata.hasdata && METAL_GetStorageMode(
texture) != MTLStorageModePrivate) {
698 desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:texture.pixelFormat
710 id<MTLTexture> stagingtex = [data.mtldevice newTextureWithDescriptor:desc];
711 if (stagingtex == nil) {
715 #if !__has_feature(objc_arc)
716 [stagingtex autorelease];
719 METAL_UploadTextureData(stagingtex, stagingrect, 0,
pixels, pitch);
721 if (
data.mtlcmdencoder != nil) {
722 [data.mtlcmdencoder endEncoding];
723 data.mtlcmdencoder = nil;
726 if (
data.mtlcmdbuffer == nil) {
727 data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
732 [blitcmd copyFromTexture:stagingtex
735 sourceOrigin:MTLOriginMake(0, 0, 0)
736 sourceSize:MTLSizeMake(rect.w, rect.h, 1)
738 destinationSlice:slice
740 destinationOrigin:MTLOriginMake(rect.x, rect.y, 0)];
742 [blitcmd endEncoding];
746 [data.mtlcmdbuffer commit];
747 data.mtlcmdbuffer = nil;
756 METAL_TextureData *texturedata = (__bridge METAL_TextureData *)
texture->driverdata;
758 if (METAL_UpdateTextureInternal(
renderer, texturedata, texturedata.mtltexture, *
rect, 0,
pixels, pitch) < 0) {
762 if (texturedata.yuv) {
765 int UVpitch = (pitch + 1) / 2;
770 if (METAL_UpdateTextureInternal(
renderer, texturedata, texturedata.mtltexture_uv, UVrect, Uslice,
pixels, UVpitch) < 0) {
776 if (METAL_UpdateTextureInternal(
renderer, texturedata, texturedata.mtltexture_uv, UVrect, Vslice,
pixels, UVpitch) < 0) {
781 if (texturedata.nv12) {
783 int UVpitch = 2 * ((pitch + 1) / 2);
787 if (METAL_UpdateTextureInternal(
renderer, texturedata, texturedata.mtltexture_uv, UVrect, 0,
pixels, UVpitch) < 0) {
792 texturedata.hasdata = YES;
800 const Uint8 *Yplane,
int Ypitch,
801 const Uint8 *Uplane,
int Upitch,
802 const Uint8 *Vplane,
int Vpitch)
804 METAL_TextureData *texturedata = (__bridge METAL_TextureData *)
texture->driverdata;
805 const int Uslice = 0;
806 const int Vslice = 1;
814 if (METAL_UpdateTextureInternal(
renderer, texturedata, texturedata.mtltexture, *
rect, 0, Yplane, Ypitch) < 0) {
817 if (METAL_UpdateTextureInternal(
renderer, texturedata, texturedata.mtltexture_uv, UVrect, Uslice, Uplane, Upitch)) {
820 if (METAL_UpdateTextureInternal(
renderer, texturedata, texturedata.mtltexture_uv, UVrect, Vslice, Vplane, Vpitch)) {
824 texturedata.hasdata = YES;
834 METAL_TextureData *texturedata = (__bridge METAL_TextureData *)
texture->driverdata;
839 return SDL_SetError(
"Invalid rectangle dimensions for LockTexture.");
844 if (texturedata.yuv || texturedata.nv12) {
845 buffersize = ((*pitch) *
rect->
h) + (2 * (*pitch + 1) / 2) * ((
rect->
h + 1) / 2);
847 buffersize = (*pitch) *
rect->
h;
850 lockedbuffer = [data.mtldevice newBufferWithLength:buffersize options:MTLResourceStorageModeShared];
851 if (lockedbuffer == nil) {
855 texturedata.lockedrect = *
rect;
856 texturedata.lockedbuffer = lockedbuffer;
857 *
pixels = [lockedbuffer contents];
860 #if !__has_feature(objc_arc)
861 [lockedbuffer release];
871 METAL_TextureData *texturedata = (__bridge METAL_TextureData *)
texture->driverdata;
876 if (texturedata.lockedbuffer == nil) {
880 if (
data.mtlcmdencoder != nil) {
881 [data.mtlcmdencoder endEncoding];
882 data.mtlcmdencoder = nil;
885 if (
data.mtlcmdbuffer == nil) {
886 data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
891 [blitcmd copyFromBuffer:texturedata.lockedbuffer
893 sourceBytesPerRow:pitch
894 sourceBytesPerImage:0
895 sourceSize:MTLSizeMake(rect.w, rect.h, 1)
896 toTexture:texturedata.mtltexture
899 destinationOrigin:MTLOriginMake(rect.x, rect.y, 0)];
901 if (texturedata.yuv) {
904 int UVpitch = (pitch + 1) / 2;
906 [blitcmd copyFromBuffer:texturedata.lockedbuffer
907 sourceOffset:rect.h * pitch
908 sourceBytesPerRow:UVpitch
909 sourceBytesPerImage:UVpitch * UVrect.h
910 sourceSize:MTLSizeMake(UVrect.w, UVrect.h, 1)
911 toTexture:texturedata.mtltexture_uv
912 destinationSlice:Uslice
914 destinationOrigin:MTLOriginMake(UVrect.x, UVrect.y, 0)];
916 [blitcmd copyFromBuffer:texturedata.lockedbuffer
917 sourceOffset:(rect.h * pitch) + UVrect.h * UVpitch
918 sourceBytesPerRow:UVpitch
919 sourceBytesPerImage:UVpitch * UVrect.h
920 sourceSize:MTLSizeMake(UVrect.w, UVrect.h, 1)
921 toTexture:texturedata.mtltexture_uv
922 destinationSlice:Vslice
924 destinationOrigin:MTLOriginMake(UVrect.x, UVrect.y, 0)];
927 if (texturedata.nv12) {
928 int UVpitch = 2 * ((pitch + 1) / 2);
930 [blitcmd copyFromBuffer:texturedata.lockedbuffer
931 sourceOffset:rect.h * pitch
932 sourceBytesPerRow:UVpitch
933 sourceBytesPerImage:0
934 sourceSize:MTLSizeMake(UVrect.w, UVrect.h, 1)
935 toTexture:texturedata.mtltexture_uv
938 destinationOrigin:MTLOriginMake(UVrect.x, UVrect.y, 0)];
941 [blitcmd endEncoding];
943 [data.mtlcmdbuffer commit];
944 data.mtlcmdbuffer = nil;
946 texturedata.lockedbuffer = nil;
947 texturedata.hasdata = YES;
954 METAL_TextureData *texturedata = (__bridge METAL_TextureData *)
texture->driverdata;
957 texturedata.mtlsampler =
data.mtlsamplernearest;
959 texturedata.mtlsampler =
data.mtlsamplerlinear;
971 [data.mtlcmdencoder endEncoding];
972 [data.mtlcmdbuffer commit];
974 data.mtlcmdencoder = nil;
975 data.mtlcmdbuffer = nil;
987 normtex(
const float _val,
const float len)
995 float projection[4][4];
998 const size_t matrixlen =
sizeof (projection);
1006 projection[0][0] = 2.0f /
w;
1007 projection[1][1] = -2.0
f /
h;
1008 projection[3][0] = -1.0
f;
1009 projection[3][1] = 1.0f;
1010 projection[3][3] = 1.0f;
1020 const size_t vertlen =
sizeof (float) * 4;
1035 const size_t vertlen = (
sizeof (float) * 2) *
count;
1048 const size_t vertlen = (
sizeof (float) * 8) *
count;
1062 if ((
rects->w <= 0.0f) || (
rects->h <= 0.0f)) {
1087 const float texw = (float)
texture->w;
1088 const float texh = (
float)
texture->h;
1090 const size_t vertlen = (
sizeof (float) * 16);
1099 *(verts++) = dstrect->
x;
1100 *(verts++) = dstrect->
y + dstrect->
h;
1101 *(verts++) = normtex(srcrect->
x, texw);
1102 *(verts++) = normtex(srcrect->
y + srcrect->
h, texh);
1104 *(verts++) = dstrect->
x;
1105 *(verts++) = dstrect->
y;
1106 *(verts++) = normtex(srcrect->
x, texw);
1107 *(verts++) = normtex(srcrect->
y, texh);
1109 *(verts++) = dstrect->
x + dstrect->
w;
1110 *(verts++) = dstrect->
y + dstrect->
h;
1111 *(verts++) = normtex(srcrect->
x + srcrect->
w, texw);
1112 *(verts++) = normtex(srcrect->
y + srcrect->
h, texh);
1114 *(verts++) = dstrect->
x + dstrect->
w;
1115 *(verts++) = dstrect->
y;
1116 *(verts++) = normtex(srcrect->
x + srcrect->
w, texw);
1117 *(verts++) = normtex(srcrect->
y, texh);
1127 const float texw = (float)
texture->
w;
1128 const float texh = (
float)
texture->h;
1129 const float rads = (float)(M_PI * (
float)
angle / 180.0f);
1130 const float c = cosf(rads),
s = sinf(rads);
1131 float minu, maxu, minv, maxv;
1132 const size_t vertlen = (
sizeof (float) * 32);
1142 SDL_memset(verts,
'\0',
sizeof (*verts) * 16);
1143 verts[10] = verts[15] = 1.0f;
1151 verts[12] = dstrect->
x + center->
x;
1152 verts[13] = dstrect->
y + center->
y;
1160 minu = normtex(srcquad->
x, texw);
1161 maxu = normtex(srcquad->
x + srcquad->
w, texw);
1162 minv = normtex(srcquad->
y, texh);
1163 maxv = normtex(srcquad->
y + srcquad->
h, texh);
1177 *(verts++) = -center->
x;
1178 *(verts++) = dstrect->
h - center->
y;
1182 *(verts++) = -center->
x;
1183 *(verts++) = -center->
y;
1187 *(verts++) = dstrect->
w - center->
x;
1188 *(verts++) = dstrect->
h - center->
y;
1192 *(verts++) = dstrect->
w - center->
x;
1193 *(verts++) = -center->
y;
1203 #if __has_feature(objc_arc)
1210 size_t constants_offset;
1217 size_t projection_offset;
1219 size_t color_offset;
1220 } METAL_DrawStateCache;
1224 const size_t constants_offset,
id<MTLBuffer> mtlbufvertex, METAL_DrawStateCache *statecache)
1231 METAL_ActivateRenderCommandEncoder(
renderer, MTLLoadActionLoad,
NULL, statecache->vertex_buffer);
1233 if (statecache->viewport_dirty) {
1235 viewport.originX = statecache->viewport.
x;
1236 viewport.originY = statecache->viewport.
y;
1237 viewport.width = statecache->viewport.
w;
1238 viewport.height = statecache->viewport.
h;
1241 [data.mtlcmdencoder setViewport:viewport];
1242 [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:statecache->projection_offset atIndex:2];
1246 if (statecache->cliprect_dirty) {
1247 MTLScissorRect mtlrect;
1248 if (statecache->cliprect_enabled) {
1250 mtlrect.
x = statecache->viewport.x +
rect->
x;
1251 mtlrect.
y = statecache->viewport.y +
rect->
y;
1252 mtlrect.width =
rect->
w;
1253 mtlrect.height =
rect->
h;
1255 mtlrect.x = statecache->viewport.x;
1256 mtlrect.y = statecache->viewport.y;
1257 mtlrect.width = statecache->viewport.w;
1258 mtlrect.height = statecache->viewport.h;
1260 if (mtlrect.width > 0 && mtlrect.height > 0) {
1261 [data.mtlcmdencoder setScissorRect:mtlrect];
1266 if (statecache->color_dirty) {
1267 [data.mtlcmdencoder setFragmentBufferOffset:statecache->color_offset atIndex:0];
1271 newpipeline = ChoosePipelineState(
data,
data.activepipelines,
shader, blend);
1272 if (newpipeline != statecache->pipeline) {
1273 [data.mtlcmdencoder setRenderPipelineState:newpipeline];
1274 statecache->pipeline = newpipeline;
1277 if (constants_offset != statecache->constants_offset) {
1278 if (constants_offset != CONSTANTS_OFFSET_INVALID) {
1279 [data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:constants_offset atIndex:3];
1281 statecache->constants_offset = constants_offset;
1284 [data.mtlcmdencoder setVertexBufferOffset:first atIndex:0];
1289 id<MTLBuffer> mtlbufvertex, METAL_DrawStateCache *statecache)
1293 METAL_TextureData *texturedata = (__bridge METAL_TextureData *)
texture->driverdata;
1295 SetDrawState(
renderer, cmd, texturedata.fragmentFunction, constants_offset, mtlbufvertex, statecache);
1297 if (
texture != statecache->texture) {
1298 METAL_TextureData *oldtexturedata =
NULL;
1299 if (statecache->texture) {
1300 oldtexturedata = (__bridge METAL_TextureData *) statecache->texture->driverdata;
1302 if (!oldtexturedata || (texturedata.mtlsampler != oldtexturedata.mtlsampler)) {
1303 [data.mtlcmdencoder setFragmentSamplerState:texturedata.mtlsampler atIndex:0];
1306 [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0];
1307 if (texturedata.yuv || texturedata.nv12) {
1308 [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture_uv atIndex:1];
1309 [data.mtlcmdencoder setFragmentBuffer:data.mtlbufconstants offset:texturedata.conversionBufferOffset atIndex:1];
1311 statecache->texture =
texture;
1317 { @autoreleasepool {
1319 METAL_DrawStateCache statecache;
1324 statecache.pipeline = nil;
1325 statecache.vertex_buffer = nil;
1326 statecache.constants_offset = CONSTANTS_OFFSET_INVALID;
1327 statecache.texture =
NULL;
1329 statecache.cliprect_dirty =
SDL_TRUE;
1330 statecache.viewport_dirty =
SDL_TRUE;
1331 statecache.projection_offset = 0;
1332 statecache.color_offset = 0;
1343 mtlbufvertex = [data.mtldevice newBufferWithLength:vertsize options:MTLResourceStorageModeShared];
1344 #if !__has_feature(objc_arc)
1345 [mtlbufvertex autorelease];
1347 mtlbufvertex.label =
@"SDL vertex data";
1348 SDL_memcpy([mtlbufvertex contents], vertices, vertsize);
1350 statecache.vertex_buffer = mtlbufvertex;
1354 [data.mtlcmdencoder endEncoding];
1355 [data.mtlcmdbuffer commit];
1356 data.mtlcmdencoder = nil;
1357 data.mtlcmdbuffer = nil;
1363 statecache.projection_offset = cmd->
data.
viewport.first;
1364 statecache.viewport_dirty =
SDL_TRUE;
1365 statecache.cliprect_dirty =
SDL_TRUE;
1371 statecache.cliprect_enabled = cmd->
data.
cliprect.enabled;
1372 statecache.cliprect_dirty =
SDL_TRUE;
1377 statecache.color_offset = cmd->
data.
color.first;
1386 if (
data.mtlcmdencoder != nil) {
1387 [data.mtlcmdencoder endEncoding];
1390 [data.mtlcmdbuffer commit];
1391 data.mtlcmdencoder = nil;
1392 data.mtlcmdbuffer = nil;
1396 statecache.pipeline = nil;
1397 statecache.constants_offset = CONSTANTS_OFFSET_INVALID;
1398 statecache.texture =
NULL;
1400 statecache.cliprect_dirty =
SDL_TRUE;
1401 statecache.viewport_dirty =
SDL_TRUE;
1407 MTLClearColor
color = MTLClearColorMake(
r / 255.0
f,
g / 255.0
f,
b / 255.0
f,
a / 255.0
f);
1410 METAL_ActivateRenderCommandEncoder(
renderer, MTLLoadActionClear, &
color, mtlbufvertex);
1418 SetDrawState(
renderer, cmd, SDL_METAL_FRAGMENT_SOLID, CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM, mtlbufvertex, &statecache);
1419 [data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];
1425 const size_t maxcount = UINT16_MAX / 4;
1426 SetDrawState(
renderer, cmd, SDL_METAL_FRAGMENT_SOLID, CONSTANTS_OFFSET_IDENTITY, mtlbufvertex, &statecache);
1428 [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
1432 for (
size_t i = 0;
i <
count;
i += maxcount) {
1435 [data.mtlcmdencoder setVertexBufferOffset:cmd->data.draw.first + i*sizeof(float)*8 atIndex:0];
1436 [data.mtlcmdencoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle
1437 indexCount:SDL_min(maxcount, count - i) * 6
1438 indexType:MTLIndexTypeUInt16
1439 indexBuffer:data.mtlbufquadindices
1440 indexBufferOffset:0];
1447 SetCopyState(
renderer, cmd, CONSTANTS_OFFSET_IDENTITY, mtlbufvertex, &statecache);
1448 [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
1453 SetCopyState(
renderer, cmd, CONSTANTS_OFFSET_INVALID, mtlbufvertex, &statecache);
1454 [data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:cmd->data.draw.count atIndex:3];
1455 [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
1471 { @autoreleasepool {
1473 METAL_ActivateRenderCommandEncoder(
renderer, MTLLoadActionLoad,
NULL, nil);
1475 [data.mtlcmdencoder endEncoding];
1483 if (METAL_GetStorageMode(mtltexture) == MTLStorageModeManaged) {
1485 [blit synchronizeResource:mtltexture];
1492 [data.mtlcmdbuffer commit];
1493 [data.mtlcmdbuffer waitUntilCompleted];
1494 data.mtlcmdencoder = nil;
1495 data.mtlcmdbuffer = nil;
1500 const int temp_pitch =
rect->
w * 4;
1506 [mtltexture getBytes:temp_pixels bytesPerRow:temp_pitch fromRegion:mtlregion mipmapLevel:0];
1516 { @autoreleasepool {
1519 if (
data.mtlcmdencoder != nil) {
1520 [data.mtlcmdencoder endEncoding];
1522 if (
data.mtlbackbuffer != nil) {
1523 [data.mtlcmdbuffer presentDrawable:data.mtlbackbuffer];
1525 if (
data.mtlcmdbuffer != nil) {
1526 [data.mtlcmdbuffer commit];
1528 data.mtlcmdencoder = nil;
1529 data.mtlcmdbuffer = nil;
1530 data.mtlbackbuffer = nil;
1535 { @autoreleasepool {
1536 CFBridgingRelease(
texture->driverdata);
1542 { @autoreleasepool {
1546 if (
data.mtlcmdencoder != nil) {
1547 [data.mtlcmdencoder endEncoding];
1550 DestroyAllPipelines(
data.allpipelines,
data.pipelinescount);
1560 { @autoreleasepool {
1562 return (__bridge
void*)
data.mtllayer;
1567 { @autoreleasepool {
1568 METAL_ActivateRenderCommandEncoder(
renderer, MTLLoadActionLoad,
NULL, nil);
1570 return (__bridge
void*)
data.mtlcmdencoder;
1575 { @autoreleasepool {
1580 CAMetalLayer *
layer = nil;
1588 if (IsMetalAvailable(&syswm) == -1) {
1599 mtldevice = MTLCreateSystemDefaultDevice();
1601 if (mtldevice == nil) {
1610 #if !__has_feature(objc_arc)
1611 [mtldevice release];
1618 data = [[METAL_RenderData alloc] init];
1621 #if !__has_feature(objc_arc)
1622 [mtldevice release];
1632 data.mtlview = view;
1635 layer = (CAMetalLayer *)[(NSView *)view
layer];
1637 layer = (CAMetalLayer *)[(__bridge UIView *)view
layer];
1640 layer.device = mtldevice;
1643 layer.framebufferOnly = NO;
1648 data.mtlcmdqueue = mtlcmdqueue;
1649 data.mtlcmdqueue.label =
@"SDL Metal Renderer";
1650 data.mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor];
1656 dispatch_data_t mtllibdata = dispatch_data_create(sdl_metallib, sdl_metallib_len, dispatch_get_global_queue(0, 0), ^{});
1657 id<MTLLibrary> mtllibrary = [data.mtldevice newLibraryWithData:mtllibdata error:&err];
1658 data.mtllibrary = mtllibrary;
1660 #if !__has_feature(objc_arc)
1661 dispatch_release(mtllibdata);
1663 data.mtllibrary.label =
@"SDL Metal renderer shader library";
1666 data.pipelinescount = 0;
1668 ChooseShaderPipelines(
data, MTLPixelFormatBGRA8Unorm);
1670 MTLSamplerDescriptor *samplerdesc = [[MTLSamplerDescriptor alloc] init];
1672 samplerdesc.minFilter = MTLSamplerMinMagFilterNearest;
1673 samplerdesc.magFilter = MTLSamplerMinMagFilterNearest;
1674 id<MTLSamplerState> mtlsamplernearest = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc];
1675 data.mtlsamplernearest = mtlsamplernearest;
1677 samplerdesc.minFilter = MTLSamplerMinMagFilterLinear;
1678 samplerdesc.magFilter = MTLSamplerMinMagFilterLinear;
1679 id<MTLSamplerState> mtlsamplerlinear = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc];
1680 data.mtlsamplerlinear = mtlsamplerlinear;
1683 float identitytransform[16] = {
1684 1.0f, 0.0f, 0.0f, 0.0f,
1685 0.0f, 1.0f, 0.0f, 0.0f,
1686 0.0f, 0.0f, 1.0f, 0.0f,
1687 0.0f, 0.0f, 0.0f, 1.0f,
1690 float halfpixeltransform[16] = {
1691 1.0f, 0.0f, 0.0f, 0.0f,
1692 0.0f, 1.0f, 0.0f, 0.0f,
1693 0.0f, 0.0f, 1.0f, 0.0f,
1694 0.5f, 0.5f, 0.0f, 1.0f,
1698 float decodetransformJPEG[4*4] = {
1699 0.0, -0.501960814, -0.501960814, 0.0,
1700 1.0000, 0.0000, 1.4020, 0.0,
1701 1.0000, -0.3441, -0.7141, 0.0,
1702 1.0000, 1.7720, 0.0000, 0.0,
1705 float decodetransformBT601[4*4] = {
1706 -0.0627451017, -0.501960814, -0.501960814, 0.0,
1707 1.1644, 0.0000, 1.5960, 0.0,
1708 1.1644, -0.3918, -0.8130, 0.0,
1709 1.1644, 2.0172, 0.0000, 0.0,
1712 float decodetransformBT709[4*4] = {
1713 0.0, -0.501960814, -0.501960814, 0.0,
1714 1.0000, 0.0000, 1.4020, 0.0,
1715 1.0000, -0.3441, -0.7141, 0.0,
1716 1.0000, 1.7720, 0.0000, 0.0,
1719 id<MTLBuffer> mtlbufconstantstaging = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModeShared];
1720 #if !__has_feature(objc_arc)
1721 [mtlbufconstantstaging autorelease];
1724 char *constantdata = [mtlbufconstantstaging contents];
1725 SDL_memcpy(constantdata + CONSTANTS_OFFSET_IDENTITY, identitytransform,
sizeof(identitytransform));
1726 SDL_memcpy(constantdata + CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM, halfpixeltransform,
sizeof(halfpixeltransform));
1727 SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_JPEG, decodetransformJPEG,
sizeof(decodetransformJPEG));
1728 SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT601, decodetransformBT601,
sizeof(decodetransformBT601));
1729 SDL_memcpy(constantdata + CONSTANTS_OFFSET_DECODE_BT709, decodetransformBT709,
sizeof(decodetransformBT709));
1731 int quadcount = UINT16_MAX / 4;
1732 size_t indicessize =
sizeof(UInt16) * quadcount * 6;
1733 id<MTLBuffer> mtlbufquadindicesstaging = [data.mtldevice newBufferWithLength:indicessize options:MTLResourceStorageModeShared];
1734 #if !__has_feature(objc_arc)
1735 [mtlbufquadindicesstaging autorelease];
1743 UInt16 *indexdata = [mtlbufquadindicesstaging contents];
1744 for (
int i = 0;
i < quadcount;
i++) {
1745 indexdata[i * 6 + 0] =
i * 4 + 0;
1746 indexdata[i * 6 + 1] =
i * 4 + 1;
1747 indexdata[i * 6 + 2] =
i * 4 + 2;
1749 indexdata[i * 6 + 3] =
i * 4 + 2;
1750 indexdata[i * 6 + 4] =
i * 4 + 1;
1751 indexdata[i * 6 + 5] =
i * 4 + 3;
1754 id<MTLBuffer> mtlbufconstants = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModePrivate];
1755 data.mtlbufconstants = mtlbufconstants;
1756 data.mtlbufconstants.label =
@"SDL constant data";
1758 id<MTLBuffer> mtlbufquadindices = [data.mtldevice newBufferWithLength:indicessize options:MTLResourceStorageModePrivate];
1759 data.mtlbufquadindices = mtlbufquadindices;
1760 data.mtlbufquadindices.label =
@"SDL quad index buffer";
1765 [blitcmd copyFromBuffer:mtlbufconstantstaging sourceOffset:0 toBuffer:mtlbufconstants destinationOffset:0 size:CONSTANTS_LENGTH];
1766 [blitcmd copyFromBuffer:mtlbufquadindicesstaging sourceOffset:0 toBuffer:mtlbufquadindices destinationOffset:0 size:indicessize];
1768 [blitcmd endEncoding];
1803 #if defined(__MACOSX__) && defined(MAC_OS_X_VERSION_10_13)
1806 if (
data.mtllayer.displaySyncEnabled) {
1816 int maxtexsize = 4096;
1817 #if defined(__MACOSX__)
1819 #elif defined(__TVOS__)
1823 if ([mtldevice supportsFeatureSet:MTLFeatureSet_tvOS_GPUFamily2_v1]) {
1829 #ifdef __IPHONE_11_0
1830 #pragma clang diagnostic push
1831 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
1832 if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily4_v1]) {
1835 #pragma clang diagnostic pop
1837 #ifdef __IPHONE_10_0
1838 if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
1842 if ([mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v2] || [mtldevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v2]) {
1852 #if !__has_feature(objc_arc)
1853 [mtlcmdqueue release];
1854 [mtllibrary release];
1855 [samplerdesc release];
1856 [mtlsamplernearest release];
1857 [mtlsamplerlinear release];
1858 [mtlbufconstants release];
1859 [mtlbufquadindices release];
1861 [mtldevice release];
1868 METAL_CreateRenderer,