Atlas - SDL_gpu_metal.m
Home / ext / SDL / src / gpu / metal Lines: 1 | Size: 182914 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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 22#include "SDL_internal.h" 23 24#ifdef SDL_GPU_METAL 25 26#include <Metal/Metal.h> 27#include <QuartzCore/CoreAnimation.h> 28 29#include "../SDL_sysgpu.h" 30 31// Defines 32 33#define METAL_FIRST_VERTEX_BUFFER_SLOT 14 34#define WINDOW_PROPERTY_DATA "SDL_GPUMetalWindowPropertyData" 35#define SDL_GPU_SHADERSTAGE_COMPUTE 2 36 37#define TRACK_RESOURCE(resource, type, array, count, capacity) \ 38 do { \ 39 Uint32 i; \ 40 \ 41 for (i = 0; i < commandBuffer->count; i += 1) { \ 42 if (commandBuffer->array[i] == (resource)) { \ 43 return; \ 44 } \ 45 } \ 46 \ 47 if (commandBuffer->count == commandBuffer->capacity) { \ 48 commandBuffer->capacity += 1; \ 49 commandBuffer->array = SDL_realloc( \ 50 commandBuffer->array, \ 51 commandBuffer->capacity * sizeof(type)); \ 52 } \ 53 commandBuffer->array[commandBuffer->count] = (resource); \ 54 commandBuffer->count += 1; \ 55 SDL_AtomicIncRef(&(resource)->referenceCount); \ 56 } while (0) 57 58#define SET_ERROR_AND_RETURN(fmt, msg, ret) \ 59 do { \ 60 if (renderer->debugMode) { \ 61 SDL_LogError(SDL_LOG_CATEGORY_GPU, fmt, msg); \ 62 } \ 63 SDL_SetError(fmt, msg); \ 64 return ret; \ 65 } while (0) 66 67#define SET_STRING_ERROR_AND_RETURN(msg, ret) SET_ERROR_AND_RETURN("%s", msg, ret) 68 69// Blit Shaders 70 71#include "Metal_Blit.h" 72 73// Forward Declarations 74 75static bool METAL_Wait(SDL_GPURenderer *driverData); 76static void METAL_ReleaseWindow( 77 SDL_GPURenderer *driverData, 78 SDL_Window *window); 79static void METAL_INTERNAL_DestroyBlitResources(SDL_GPURenderer *driverData); 80 81// Conversions 82 83#define RETURN_FORMAT(availability, format) \ 84 if (availability) { return format; } else { return MTLPixelFormatInvalid; } 85 86static MTLPixelFormat SDLToMetal_TextureFormat(SDL_GPUTextureFormat format) 87{ 88 switch (format) { 89 case SDL_GPU_TEXTUREFORMAT_INVALID: return MTLPixelFormatInvalid; 90 case SDL_GPU_TEXTUREFORMAT_A8_UNORM: return MTLPixelFormatA8Unorm; 91 case SDL_GPU_TEXTUREFORMAT_R8_UNORM: return MTLPixelFormatR8Unorm; 92 case SDL_GPU_TEXTUREFORMAT_R8G8_UNORM: return MTLPixelFormatRG8Unorm; 93 case SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM: return MTLPixelFormatRGBA8Unorm; 94 case SDL_GPU_TEXTUREFORMAT_R16_UNORM: return MTLPixelFormatR16Unorm; 95 case SDL_GPU_TEXTUREFORMAT_R16G16_UNORM: return MTLPixelFormatRG16Unorm; 96 case SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UNORM: return MTLPixelFormatRGBA16Unorm; 97 case SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM: return MTLPixelFormatRGB10A2Unorm; 98 case SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatB5G6R5Unorm); 99 case SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatBGR5A1Unorm); 100 case SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatABGR4Unorm); 101 case SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM: return MTLPixelFormatBGRA8Unorm; 102 case SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM: RETURN_FORMAT(@available(iOS 16.4, tvOS 16.4, *), MTLPixelFormatBC1_RGBA); 103 case SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM: RETURN_FORMAT(@available(iOS 16.4, tvOS 16.4, *), MTLPixelFormatBC2_RGBA); 104 case SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM: RETURN_FORMAT(@available(iOS 16.4, tvOS 16.4, *), MTLPixelFormatBC3_RGBA); 105 case SDL_GPU_TEXTUREFORMAT_BC4_R_UNORM: RETURN_FORMAT(@available(iOS 16.4, tvOS 16.4, *), MTLPixelFormatBC4_RUnorm); 106 case SDL_GPU_TEXTUREFORMAT_BC5_RG_UNORM: RETURN_FORMAT(@available(iOS 16.4, tvOS 16.4, *), MTLPixelFormatBC5_RGUnorm); 107 case SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM: RETURN_FORMAT(@available(iOS 16.4, tvOS 16.4, *), MTLPixelFormatBC7_RGBAUnorm); 108 case SDL_GPU_TEXTUREFORMAT_BC6H_RGB_FLOAT: RETURN_FORMAT(@available(iOS 16.4, tvOS 16.4, *), MTLPixelFormatBC6H_RGBFloat); 109 case SDL_GPU_TEXTUREFORMAT_BC6H_RGB_UFLOAT: RETURN_FORMAT(@available(iOS 16.4, tvOS 16.4, *), MTLPixelFormatBC6H_RGBUfloat); 110 case SDL_GPU_TEXTUREFORMAT_R8_SNORM: return MTLPixelFormatR8Snorm; 111 case SDL_GPU_TEXTUREFORMAT_R8G8_SNORM: return MTLPixelFormatRG8Snorm; 112 case SDL_GPU_TEXTUREFORMAT_R8G8B8A8_SNORM: return MTLPixelFormatRGBA8Snorm; 113 case SDL_GPU_TEXTUREFORMAT_R16_SNORM: return MTLPixelFormatR16Snorm; 114 case SDL_GPU_TEXTUREFORMAT_R16G16_SNORM: return MTLPixelFormatRG16Snorm; 115 case SDL_GPU_TEXTUREFORMAT_R16G16B16A16_SNORM: return MTLPixelFormatRGBA16Snorm; 116 case SDL_GPU_TEXTUREFORMAT_R16_FLOAT: return MTLPixelFormatR16Float; 117 case SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT: return MTLPixelFormatRG16Float; 118 case SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT: return MTLPixelFormatRGBA16Float; 119 case SDL_GPU_TEXTUREFORMAT_R32_FLOAT: return MTLPixelFormatR32Float; 120 case SDL_GPU_TEXTUREFORMAT_R32G32_FLOAT: return MTLPixelFormatRG32Float; 121 case SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT: return MTLPixelFormatRGBA32Float; 122 case SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT: return MTLPixelFormatRG11B10Float; 123 case SDL_GPU_TEXTUREFORMAT_R8_UINT: return MTLPixelFormatR8Uint; 124 case SDL_GPU_TEXTUREFORMAT_R8G8_UINT: return MTLPixelFormatRG8Uint; 125 case SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT: return MTLPixelFormatRGBA8Uint; 126 case SDL_GPU_TEXTUREFORMAT_R16_UINT: return MTLPixelFormatR16Uint; 127 case SDL_GPU_TEXTUREFORMAT_R16G16_UINT: return MTLPixelFormatRG16Uint; 128 case SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UINT: return MTLPixelFormatRGBA16Uint; 129 case SDL_GPU_TEXTUREFORMAT_R32_UINT: return MTLPixelFormatR32Uint; 130 case SDL_GPU_TEXTUREFORMAT_R32G32_UINT: return MTLPixelFormatRG32Uint; 131 case SDL_GPU_TEXTUREFORMAT_R32G32B32A32_UINT: return MTLPixelFormatRGBA32Uint; 132 case SDL_GPU_TEXTUREFORMAT_R8_INT: return MTLPixelFormatR8Sint; 133 case SDL_GPU_TEXTUREFORMAT_R8G8_INT: return MTLPixelFormatRG8Sint; 134 case SDL_GPU_TEXTUREFORMAT_R8G8B8A8_INT: return MTLPixelFormatRGBA8Sint; 135 case SDL_GPU_TEXTUREFORMAT_R16_INT: return MTLPixelFormatR16Sint; 136 case SDL_GPU_TEXTUREFORMAT_R16G16_INT: return MTLPixelFormatRG16Sint; 137 case SDL_GPU_TEXTUREFORMAT_R16G16B16A16_INT: return MTLPixelFormatRGBA16Sint; 138 case SDL_GPU_TEXTUREFORMAT_R32_INT: return MTLPixelFormatR32Sint; 139 case SDL_GPU_TEXTUREFORMAT_R32G32_INT: return MTLPixelFormatRG32Sint; 140 case SDL_GPU_TEXTUREFORMAT_R32G32B32A32_INT: return MTLPixelFormatRGBA32Sint; 141 case SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB: return MTLPixelFormatRGBA8Unorm_sRGB; 142 case SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB: return MTLPixelFormatBGRA8Unorm_sRGB; 143 case SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM_SRGB: RETURN_FORMAT(@available(iOS 16.4, tvOS 16.4, *), MTLPixelFormatBC1_RGBA_sRGB); 144 case SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM_SRGB: RETURN_FORMAT(@available(iOS 16.4, tvOS 16.4, *), MTLPixelFormatBC2_RGBA_sRGB); 145 case SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM_SRGB: RETURN_FORMAT(@available(iOS 16.4, tvOS 16.4, *), MTLPixelFormatBC3_RGBA_sRGB); 146 case SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM_SRGB: RETURN_FORMAT(@available(iOS 16.4, tvOS 16.4, *), MTLPixelFormatBC7_RGBAUnorm_sRGB); 147 case SDL_GPU_TEXTUREFORMAT_D16_UNORM: RETURN_FORMAT(@available(iOS 13.0, tvOS 13.0, *), MTLPixelFormatDepth16Unorm); 148 case SDL_GPU_TEXTUREFORMAT_D24_UNORM: 149#ifdef SDL_PLATFORM_MACOS 150 return MTLPixelFormatDepth24Unorm_Stencil8; 151#else 152 return MTLPixelFormatInvalid; 153#endif 154 case SDL_GPU_TEXTUREFORMAT_D32_FLOAT: return MTLPixelFormatDepth32Float; 155 case SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT: 156#ifdef SDL_PLATFORM_MACOS 157 return MTLPixelFormatDepth24Unorm_Stencil8; 158#else 159 return MTLPixelFormatInvalid; 160#endif 161 case SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT: return MTLPixelFormatDepth32Float_Stencil8; 162 case SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_4x4_LDR); 163 case SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_5x4_LDR); 164 case SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_5x5_LDR); 165 case SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_6x5_LDR); 166 case SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_6x6_LDR); 167 case SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_8x5_LDR); 168 case SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_8x6_LDR); 169 case SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_8x8_LDR); 170 case SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_10x5_LDR); 171 case SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_10x6_LDR); 172 case SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_10x8_LDR); 173 case SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_10x10_LDR); 174 case SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_12x10_LDR); 175 case SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_12x12_LDR); 176 case SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM_SRGB: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_4x4_sRGB); 177 case SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM_SRGB: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_5x4_sRGB); 178 case SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM_SRGB: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_5x5_sRGB); 179 case SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM_SRGB: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_6x5_sRGB); 180 case SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM_SRGB: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_6x6_sRGB); 181 case SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM_SRGB: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_8x5_sRGB); 182 case SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM_SRGB: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_8x6_sRGB); 183 case SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM_SRGB: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_8x8_sRGB); 184 case SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM_SRGB: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_10x5_sRGB); 185 case SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM_SRGB: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_10x6_sRGB); 186 case SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM_SRGB: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_10x8_sRGB); 187 case SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM_SRGB: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_10x10_sRGB); 188 case SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM_SRGB: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_12x10_sRGB); 189 case SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM_SRGB: RETURN_FORMAT(@available(macOS 11.0, *), MTLPixelFormatASTC_12x12_sRGB); 190 case SDL_GPU_TEXTUREFORMAT_ASTC_4x4_FLOAT: RETURN_FORMAT(@available(macOS 11.0, iOS 13.0, tvOS 16.0, *), MTLPixelFormatASTC_4x4_HDR); 191 case SDL_GPU_TEXTUREFORMAT_ASTC_5x4_FLOAT: RETURN_FORMAT(@available(macOS 11.0, iOS 13.0, tvOS 16.0, *), MTLPixelFormatASTC_5x4_HDR); 192 case SDL_GPU_TEXTUREFORMAT_ASTC_5x5_FLOAT: RETURN_FORMAT(@available(macOS 11.0, iOS 13.0, tvOS 16.0, *), MTLPixelFormatASTC_5x5_HDR); 193 case SDL_GPU_TEXTUREFORMAT_ASTC_6x5_FLOAT: RETURN_FORMAT(@available(macOS 11.0, iOS 13.0, tvOS 16.0, *), MTLPixelFormatASTC_6x5_HDR); 194 case SDL_GPU_TEXTUREFORMAT_ASTC_6x6_FLOAT: RETURN_FORMAT(@available(macOS 11.0, iOS 13.0, tvOS 16.0, *), MTLPixelFormatASTC_6x6_HDR); 195 case SDL_GPU_TEXTUREFORMAT_ASTC_8x5_FLOAT: RETURN_FORMAT(@available(macOS 11.0, iOS 13.0, tvOS 16.0, *), MTLPixelFormatASTC_8x5_HDR); 196 case SDL_GPU_TEXTUREFORMAT_ASTC_8x6_FLOAT: RETURN_FORMAT(@available(macOS 11.0, iOS 13.0, tvOS 16.0, *), MTLPixelFormatASTC_8x6_HDR); 197 case SDL_GPU_TEXTUREFORMAT_ASTC_8x8_FLOAT: RETURN_FORMAT(@available(macOS 11.0, iOS 13.0, tvOS 16.0, *), MTLPixelFormatASTC_8x8_HDR); 198 case SDL_GPU_TEXTUREFORMAT_ASTC_10x5_FLOAT: RETURN_FORMAT(@available(macOS 11.0, iOS 13.0, tvOS 16.0, *), MTLPixelFormatASTC_10x5_HDR); 199 case SDL_GPU_TEXTUREFORMAT_ASTC_10x6_FLOAT: RETURN_FORMAT(@available(macOS 11.0, iOS 13.0, tvOS 16.0, *), MTLPixelFormatASTC_10x6_HDR); 200 case SDL_GPU_TEXTUREFORMAT_ASTC_10x8_FLOAT: RETURN_FORMAT(@available(macOS 11.0, iOS 13.0, tvOS 16.0, *), MTLPixelFormatASTC_10x8_HDR); 201 case SDL_GPU_TEXTUREFORMAT_ASTC_10x10_FLOAT: RETURN_FORMAT(@available(macOS 11.0, iOS 13.0, tvOS 16.0, *), MTLPixelFormatASTC_10x10_HDR); 202 case SDL_GPU_TEXTUREFORMAT_ASTC_12x10_FLOAT: RETURN_FORMAT(@available(macOS 11.0, iOS 13.0, tvOS 16.0, *), MTLPixelFormatASTC_12x10_HDR); 203 case SDL_GPU_TEXTUREFORMAT_ASTC_12x12_FLOAT: RETURN_FORMAT(@available(macOS 11.0, iOS 13.0, tvOS 16.0, *), MTLPixelFormatASTC_12x12_HDR); 204 } 205} 206 207#undef RETURN_FORMAT 208 209static MTLVertexFormat SDLToMetal_VertexFormat[] = { 210 MTLVertexFormatInvalid, // INVALID 211 MTLVertexFormatInt, // INT 212 MTLVertexFormatInt2, // INT2 213 MTLVertexFormatInt3, // INT3 214 MTLVertexFormatInt4, // INT4 215 MTLVertexFormatUInt, // UINT 216 MTLVertexFormatUInt2, // UINT2 217 MTLVertexFormatUInt3, // UINT3 218 MTLVertexFormatUInt4, // UINT4 219 MTLVertexFormatFloat, // FLOAT 220 MTLVertexFormatFloat2, // FLOAT2 221 MTLVertexFormatFloat3, // FLOAT3 222 MTLVertexFormatFloat4, // FLOAT4 223 MTLVertexFormatChar2, // BYTE2 224 MTLVertexFormatChar4, // BYTE4 225 MTLVertexFormatUChar2, // UBYTE2 226 MTLVertexFormatUChar4, // UBYTE4 227 MTLVertexFormatChar2Normalized, // BYTE2_NORM 228 MTLVertexFormatChar4Normalized, // BYTE4_NORM 229 MTLVertexFormatUChar2Normalized, // UBYTE2_NORM 230 MTLVertexFormatUChar4Normalized, // UBYTE4_NORM 231 MTLVertexFormatShort2, // SHORT2 232 MTLVertexFormatShort4, // SHORT4 233 MTLVertexFormatUShort2, // USHORT2 234 MTLVertexFormatUShort4, // USHORT4 235 MTLVertexFormatShort2Normalized, // SHORT2_NORM 236 MTLVertexFormatShort4Normalized, // SHORT4_NORM 237 MTLVertexFormatUShort2Normalized, // USHORT2_NORM 238 MTLVertexFormatUShort4Normalized, // USHORT4_NORM 239 MTLVertexFormatHalf2, // HALF2 240 MTLVertexFormatHalf4 // HALF4 241}; 242SDL_COMPILE_TIME_ASSERT(SDLToMetal_VertexFormat, SDL_arraysize(SDLToMetal_VertexFormat) == SDL_GPU_VERTEXELEMENTFORMAT_MAX_ENUM_VALUE); 243 244static MTLIndexType SDLToMetal_IndexType[] = { 245 MTLIndexTypeUInt16, // 16BIT 246 MTLIndexTypeUInt32, // 32BIT 247}; 248 249static MTLPrimitiveType SDLToMetal_PrimitiveType[] = { 250 MTLPrimitiveTypeTriangle, // TRIANGLELIST 251 MTLPrimitiveTypeTriangleStrip, // TRIANGLESTRIP 252 MTLPrimitiveTypeLine, // LINELIST 253 MTLPrimitiveTypeLineStrip, // LINESTRIP 254 MTLPrimitiveTypePoint // POINTLIST 255}; 256 257static MTLTriangleFillMode SDLToMetal_PolygonMode[] = { 258 MTLTriangleFillModeFill, // FILL 259 MTLTriangleFillModeLines, // LINE 260}; 261 262static MTLCullMode SDLToMetal_CullMode[] = { 263 MTLCullModeNone, // NONE 264 MTLCullModeFront, // FRONT 265 MTLCullModeBack, // BACK 266}; 267 268static MTLWinding SDLToMetal_FrontFace[] = { 269 MTLWindingCounterClockwise, // COUNTER_CLOCKWISE 270 MTLWindingClockwise, // CLOCKWISE 271}; 272 273static MTLBlendFactor SDLToMetal_BlendFactor[] = { 274 MTLBlendFactorZero, // INVALID 275 MTLBlendFactorZero, // ZERO 276 MTLBlendFactorOne, // ONE 277 MTLBlendFactorSourceColor, // SRC_COLOR 278 MTLBlendFactorOneMinusSourceColor, // ONE_MINUS_SRC_COLOR 279 MTLBlendFactorDestinationColor, // DST_COLOR 280 MTLBlendFactorOneMinusDestinationColor, // ONE_MINUS_DST_COLOR 281 MTLBlendFactorSourceAlpha, // SRC_ALPHA 282 MTLBlendFactorOneMinusSourceAlpha, // ONE_MINUS_SRC_ALPHA 283 MTLBlendFactorDestinationAlpha, // DST_ALPHA 284 MTLBlendFactorOneMinusDestinationAlpha, // ONE_MINUS_DST_ALPHA 285 MTLBlendFactorBlendColor, // CONSTANT_COLOR 286 MTLBlendFactorOneMinusBlendColor, // ONE_MINUS_CONSTANT_COLOR 287 MTLBlendFactorSourceAlphaSaturated, // SRC_ALPHA_SATURATE 288}; 289SDL_COMPILE_TIME_ASSERT(SDLToMetal_BlendFactor, SDL_arraysize(SDLToMetal_BlendFactor) == SDL_GPU_BLENDFACTOR_MAX_ENUM_VALUE); 290 291static MTLBlendOperation SDLToMetal_BlendOp[] = { 292 MTLBlendOperationAdd, // INVALID 293 MTLBlendOperationAdd, // ADD 294 MTLBlendOperationSubtract, // SUBTRACT 295 MTLBlendOperationReverseSubtract, // REVERSE_SUBTRACT 296 MTLBlendOperationMin, // MIN 297 MTLBlendOperationMax, // MAX 298}; 299SDL_COMPILE_TIME_ASSERT(SDLToMetal_BlendOp, SDL_arraysize(SDLToMetal_BlendOp) == SDL_GPU_BLENDOP_MAX_ENUM_VALUE); 300 301static MTLCompareFunction SDLToMetal_CompareOp[] = { 302 MTLCompareFunctionNever, // INVALID 303 MTLCompareFunctionNever, // NEVER 304 MTLCompareFunctionLess, // LESS 305 MTLCompareFunctionEqual, // EQUAL 306 MTLCompareFunctionLessEqual, // LESS_OR_EQUAL 307 MTLCompareFunctionGreater, // GREATER 308 MTLCompareFunctionNotEqual, // NOT_EQUAL 309 MTLCompareFunctionGreaterEqual, // GREATER_OR_EQUAL 310 MTLCompareFunctionAlways, // ALWAYS 311}; 312SDL_COMPILE_TIME_ASSERT(SDLToMetal_CompareOp, SDL_arraysize(SDLToMetal_CompareOp) == SDL_GPU_COMPAREOP_MAX_ENUM_VALUE); 313 314static MTLStencilOperation SDLToMetal_StencilOp[] = { 315 MTLStencilOperationKeep, // INVALID 316 MTLStencilOperationKeep, // KEEP 317 MTLStencilOperationZero, // ZERO 318 MTLStencilOperationReplace, // REPLACE 319 MTLStencilOperationIncrementClamp, // INCREMENT_AND_CLAMP 320 MTLStencilOperationDecrementClamp, // DECREMENT_AND_CLAMP 321 MTLStencilOperationInvert, // INVERT 322 MTLStencilOperationIncrementWrap, // INCREMENT_AND_WRAP 323 MTLStencilOperationDecrementWrap, // DECREMENT_AND_WRAP 324}; 325SDL_COMPILE_TIME_ASSERT(SDLToMetal_StencilOp, SDL_arraysize(SDLToMetal_StencilOp) == SDL_GPU_STENCILOP_MAX_ENUM_VALUE); 326 327static MTLSamplerAddressMode SDLToMetal_SamplerAddressMode[] = { 328 MTLSamplerAddressModeRepeat, // REPEAT 329 MTLSamplerAddressModeMirrorRepeat, // MIRRORED_REPEAT 330 MTLSamplerAddressModeClampToEdge // CLAMP_TO_EDGE 331}; 332 333static MTLSamplerMinMagFilter SDLToMetal_MinMagFilter[] = { 334 MTLSamplerMinMagFilterNearest, // NEAREST 335 MTLSamplerMinMagFilterLinear, // LINEAR 336}; 337 338static MTLSamplerMipFilter SDLToMetal_MipFilter[] = { 339 MTLSamplerMipFilterNearest, // NEAREST 340 MTLSamplerMipFilterLinear, // LINEAR 341}; 342 343static MTLLoadAction SDLToMetal_LoadOp[] = { 344 MTLLoadActionLoad, // LOAD 345 MTLLoadActionClear, // CLEAR 346 MTLLoadActionDontCare, // DONT_CARE 347}; 348 349static MTLStoreAction SDLToMetal_StoreOp[] = { 350 MTLStoreActionStore, 351 MTLStoreActionDontCare, 352 MTLStoreActionMultisampleResolve, 353 MTLStoreActionStoreAndMultisampleResolve 354}; 355 356static MTLVertexStepFunction SDLToMetal_StepFunction[] = { 357 MTLVertexStepFunctionPerVertex, 358 MTLVertexStepFunctionPerInstance, 359}; 360 361static NSUInteger SDLToMetal_SampleCount[] = { 362 1, // SDL_GPU_SAMPLECOUNT_1 363 2, // SDL_GPU_SAMPLECOUNT_2 364 4, // SDL_GPU_SAMPLECOUNT_4 365 8 // SDL_GPU_SAMPLECOUNT_8 366}; 367 368static SDL_GPUTextureFormat SwapchainCompositionToFormat[] = { 369 SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM, // SDR 370 SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB, // SDR_LINEAR 371 SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT, // HDR_EXTENDED_LINEAR 372 SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM, // HDR10_ST2084 373}; 374 375static CFStringRef SwapchainCompositionToColorSpace[4]; // initialized on device creation 376 377static MTLTextureType SDLToMetal_TextureType(SDL_GPUTextureType textureType, bool isMSAA) 378{ 379 switch (textureType) { 380 case SDL_GPU_TEXTURETYPE_2D: 381 return isMSAA ? MTLTextureType2DMultisample : MTLTextureType2D; 382 case SDL_GPU_TEXTURETYPE_2D_ARRAY: 383 return MTLTextureType2DArray; 384 case SDL_GPU_TEXTURETYPE_3D: 385 return MTLTextureType3D; 386 case SDL_GPU_TEXTURETYPE_CUBE: 387 return MTLTextureTypeCube; 388 case SDL_GPU_TEXTURETYPE_CUBE_ARRAY: 389 return MTLTextureTypeCubeArray; 390 default: 391 return MTLTextureType2D; 392 } 393} 394 395static MTLColorWriteMask SDLToMetal_ColorWriteMask( 396 SDL_GPUColorComponentFlags mask) 397{ 398 MTLColorWriteMask result = 0; 399 if (mask & SDL_GPU_COLORCOMPONENT_R) { 400 result |= MTLColorWriteMaskRed; 401 } 402 if (mask & SDL_GPU_COLORCOMPONENT_G) { 403 result |= MTLColorWriteMaskGreen; 404 } 405 if (mask & SDL_GPU_COLORCOMPONENT_B) { 406 result |= MTLColorWriteMaskBlue; 407 } 408 if (mask & SDL_GPU_COLORCOMPONENT_A) { 409 result |= MTLColorWriteMaskAlpha; 410 } 411 return result; 412} 413 414static MTLDepthClipMode SDLToMetal_DepthClipMode( 415 bool enableDepthClip 416) { 417 if (enableDepthClip) { 418 return MTLDepthClipModeClip; 419 } else { 420 return MTLDepthClipModeClamp; 421 } 422} 423 424// Structs 425 426typedef struct MetalTexture 427{ 428 id<MTLTexture> handle; 429 SDL_AtomicInt referenceCount; 430} MetalTexture; 431 432typedef struct MetalTextureContainer 433{ 434 TextureCommonHeader header; 435 436 MetalTexture *activeTexture; 437 Uint8 canBeCycled; 438 439 Uint32 textureCapacity; 440 Uint32 textureCount; 441 MetalTexture **textures; 442 443 char *debugName; 444} MetalTextureContainer; 445 446typedef struct MetalFence 447{ 448 SDL_AtomicInt complete; 449 SDL_AtomicInt referenceCount; 450} MetalFence; 451 452typedef struct MetalWindowData 453{ 454 SDL_Window *window; 455 SDL_MetalView view; 456 CAMetalLayer *layer; 457 SDL_GPUPresentMode presentMode; 458 id<CAMetalDrawable> drawable; 459 MetalTexture texture; 460 MetalTextureContainer textureContainer; 461 SDL_GPUFence *inFlightFences[MAX_FRAMES_IN_FLIGHT]; 462 Uint32 frameCounter; 463} MetalWindowData; 464 465typedef struct MetalShader 466{ 467 id<MTLLibrary> library; 468 id<MTLFunction> function; 469 470 SDL_GPUShaderStage stage; 471 Uint32 numSamplers; 472 Uint32 numUniformBuffers; 473 Uint32 numStorageBuffers; 474 Uint32 numStorageTextures; 475} MetalShader; 476 477typedef struct MetalGraphicsPipeline 478{ 479 GraphicsPipelineCommonHeader header; 480 481 id<MTLRenderPipelineState> handle; 482 483 SDL_GPURasterizerState rasterizerState; 484 SDL_GPUPrimitiveType primitiveType; 485 486 id<MTLDepthStencilState> depth_stencil_state; 487} MetalGraphicsPipeline; 488 489typedef struct MetalComputePipeline 490{ 491 ComputePipelineCommonHeader header; 492 493 id<MTLComputePipelineState> handle; 494 Uint32 threadcountX; 495 Uint32 threadcountY; 496 Uint32 threadcountZ; 497} MetalComputePipeline; 498 499typedef struct MetalBuffer 500{ 501 id<MTLBuffer> handle; 502 SDL_AtomicInt referenceCount; 503} MetalBuffer; 504 505typedef struct MetalBufferContainer 506{ 507 MetalBuffer *activeBuffer; 508 Uint32 size; 509 510 Uint32 bufferCapacity; 511 Uint32 bufferCount; 512 MetalBuffer **buffers; 513 514 bool isPrivate; 515 bool isWriteOnly; 516 char *debugName; 517} MetalBufferContainer; 518 519typedef struct MetalUniformBuffer 520{ 521 id<MTLBuffer> handle; 522 Uint32 writeOffset; 523 Uint32 drawOffset; 524} MetalUniformBuffer; 525 526typedef struct MetalRenderer MetalRenderer; 527 528typedef struct MetalCommandBuffer 529{ 530 CommandBufferCommonHeader common; 531 MetalRenderer *renderer; 532 533 // Native Handle 534 id<MTLCommandBuffer> handle; 535 536 // Presentation 537 MetalWindowData **windowDatas; 538 Uint32 windowDataCount; 539 Uint32 windowDataCapacity; 540 541 // Render Pass 542 id<MTLRenderCommandEncoder> renderEncoder; 543 MetalGraphicsPipeline *graphics_pipeline; 544 MetalBuffer *indexBuffer; 545 Uint32 indexBufferOffset; 546 SDL_GPUIndexElementSize index_element_size; 547 548 // Copy Pass 549 id<MTLBlitCommandEncoder> blitEncoder; 550 551 // Compute Pass 552 id<MTLComputeCommandEncoder> computeEncoder; 553 MetalComputePipeline *compute_pipeline; 554 555 // Resource slot state 556 bool needVertexBufferBind; 557 bool needVertexSamplerBind; 558 bool needVertexStorageTextureBind; 559 bool needVertexStorageBufferBind; 560 bool needVertexUniformBufferBind[MAX_UNIFORM_BUFFERS_PER_STAGE]; 561 562 bool needFragmentSamplerBind; 563 bool needFragmentStorageTextureBind; 564 bool needFragmentStorageBufferBind; 565 bool needFragmentUniformBufferBind[MAX_UNIFORM_BUFFERS_PER_STAGE]; 566 567 bool needComputeSamplerBind; 568 bool needComputeReadOnlyStorageTextureBind; 569 bool needComputeReadOnlyStorageBufferBind; 570 bool needComputeUniformBufferBind[MAX_UNIFORM_BUFFERS_PER_STAGE]; 571 572 id<MTLBuffer> vertexBuffers[MAX_VERTEX_BUFFERS]; 573 Uint32 vertexBufferOffsets[MAX_VERTEX_BUFFERS]; 574 Uint32 vertexBufferCount; 575 576 id<MTLSamplerState> vertexSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE]; 577 id<MTLTexture> vertexTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE]; 578 id<MTLTexture> vertexStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE]; 579 id<MTLBuffer> vertexStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE]; 580 MetalUniformBuffer *vertexUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE]; 581 582 id<MTLSamplerState> fragmentSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE]; 583 id<MTLTexture> fragmentTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE]; 584 id<MTLTexture> fragmentStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE]; 585 id<MTLBuffer> fragmentStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE]; 586 MetalUniformBuffer *fragmentUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE]; 587 588 id<MTLTexture> computeSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE]; 589 id<MTLSamplerState> computeSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE]; 590 id<MTLTexture> computeReadOnlyTextures[MAX_STORAGE_TEXTURES_PER_STAGE]; 591 id<MTLBuffer> computeReadOnlyBuffers[MAX_STORAGE_BUFFERS_PER_STAGE]; 592 id<MTLTexture> computeReadWriteTextures[MAX_COMPUTE_WRITE_TEXTURES]; 593 id<MTLBuffer> computeReadWriteBuffers[MAX_COMPUTE_WRITE_BUFFERS]; 594 MetalUniformBuffer *computeUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE]; 595 596 MetalUniformBuffer **usedUniformBuffers; 597 Uint32 usedUniformBufferCount; 598 Uint32 usedUniformBufferCapacity; 599 600 // Fences 601 MetalFence *fence; 602 bool autoReleaseFence; 603 604 // Reference Counting 605 MetalBuffer **usedBuffers; 606 Uint32 usedBufferCount; 607 Uint32 usedBufferCapacity; 608 609 MetalTexture **usedTextures; 610 Uint32 usedTextureCount; 611 Uint32 usedTextureCapacity; 612} MetalCommandBuffer; 613 614typedef struct MetalSampler 615{ 616 id<MTLSamplerState> handle; 617} MetalSampler; 618 619typedef struct BlitPipeline 620{ 621 SDL_GPUGraphicsPipeline *pipeline; 622 SDL_GPUTextureFormat format; 623} BlitPipeline; 624 625struct MetalRenderer 626{ 627 // Reference to the parent device 628 SDL_GPUDevice *sdlGPUDevice; 629 630 id<MTLDevice> device; 631 id<MTLCommandQueue> queue; 632 633 bool debugMode; 634 SDL_PropertiesID props; 635 Uint32 allowedFramesInFlight; 636 637 MetalWindowData **claimedWindows; 638 Uint32 claimedWindowCount; 639 Uint32 claimedWindowCapacity; 640 641 MetalCommandBuffer **availableCommandBuffers; 642 Uint32 availableCommandBufferCount; 643 Uint32 availableCommandBufferCapacity; 644 645 MetalCommandBuffer **submittedCommandBuffers; 646 Uint32 submittedCommandBufferCount; 647 Uint32 submittedCommandBufferCapacity; 648 649 MetalFence **availableFences; 650 Uint32 availableFenceCount; 651 Uint32 availableFenceCapacity; 652 653 MetalUniformBuffer **uniformBufferPool; 654 Uint32 uniformBufferPoolCount; 655 Uint32 uniformBufferPoolCapacity; 656 657 MetalBufferContainer **bufferContainersToDestroy; 658 Uint32 bufferContainersToDestroyCount; 659 Uint32 bufferContainersToDestroyCapacity; 660 661 MetalTextureContainer **textureContainersToDestroy; 662 Uint32 textureContainersToDestroyCount; 663 Uint32 textureContainersToDestroyCapacity; 664 665 // Blit 666 SDL_GPUShader *blitVertexShader; 667 SDL_GPUShader *blitFrom2DShader; 668 SDL_GPUShader *blitFrom2DArrayShader; 669 SDL_GPUShader *blitFrom3DShader; 670 SDL_GPUShader *blitFromCubeShader; 671 SDL_GPUShader *blitFromCubeArrayShader; 672 673 SDL_GPUSampler *blitNearestSampler; 674 SDL_GPUSampler *blitLinearSampler; 675 676 BlitPipelineCacheEntry *blitPipelines; 677 Uint32 blitPipelineCount; 678 Uint32 blitPipelineCapacity; 679 680 // Mutexes 681 SDL_Mutex *submitLock; 682 SDL_Mutex *acquireCommandBufferLock; 683 SDL_Mutex *acquireUniformBufferLock; 684 SDL_Mutex *disposeLock; 685 SDL_Mutex *fenceLock; 686 SDL_Mutex *windowLock; 687}; 688 689// Helper Functions 690 691// FIXME: This should be moved into SDL_sysgpu.h 692static inline Uint32 METAL_INTERNAL_NextHighestAlignment( 693 Uint32 n, 694 Uint32 align) 695{ 696 return align * ((n + align - 1) / align); 697} 698 699// Quit 700 701static void METAL_DestroyDevice(SDL_GPUDevice *device) 702{ 703 MetalRenderer *renderer = (MetalRenderer *)device->driverData; 704 705 // Flush any remaining GPU work... 706 METAL_Wait(device->driverData); 707 708 // Release the window data 709 for (Sint32 i = renderer->claimedWindowCount - 1; i >= 0; i -= 1) { 710 METAL_ReleaseWindow(device->driverData, renderer->claimedWindows[i]->window); 711 } 712 SDL_free(renderer->claimedWindows); 713 714 // Release the blit resources 715 METAL_INTERNAL_DestroyBlitResources(device->driverData); 716 717 // Release uniform buffers 718 for (Uint32 i = 0; i < renderer->uniformBufferPoolCount; i += 1) { 719 renderer->uniformBufferPool[i]->handle = nil; 720 SDL_free(renderer->uniformBufferPool[i]); 721 } 722 SDL_free(renderer->uniformBufferPool); 723 724 // Release destroyed resource lists 725 SDL_free(renderer->bufferContainersToDestroy); 726 SDL_free(renderer->textureContainersToDestroy); 727 728 // Release command buffer infrastructure 729 for (Uint32 i = 0; i < renderer->availableCommandBufferCount; i += 1) { 730 MetalCommandBuffer *commandBuffer = renderer->availableCommandBuffers[i]; 731 SDL_free(commandBuffer->usedBuffers); 732 SDL_free(commandBuffer->usedTextures); 733 SDL_free(commandBuffer->usedUniformBuffers); 734 SDL_free(commandBuffer->windowDatas); 735 SDL_free(commandBuffer); 736 } 737 SDL_free(renderer->availableCommandBuffers); 738 SDL_free(renderer->submittedCommandBuffers); 739 740 // Release fence infrastructure 741 for (Uint32 i = 0; i < renderer->availableFenceCount; i += 1) { 742 SDL_free(renderer->availableFences[i]); 743 } 744 SDL_free(renderer->availableFences); 745 746 // Release the mutexes 747 SDL_DestroyMutex(renderer->submitLock); 748 SDL_DestroyMutex(renderer->acquireCommandBufferLock); 749 SDL_DestroyMutex(renderer->acquireUniformBufferLock); 750 SDL_DestroyMutex(renderer->disposeLock); 751 SDL_DestroyMutex(renderer->fenceLock); 752 SDL_DestroyMutex(renderer->windowLock); 753 754 // Release the command queue 755 renderer->queue = nil; 756 757 // Release properties 758 SDL_DestroyProperties(renderer->props); 759 760 // Free the primary structures 761 SDL_free(renderer); 762 SDL_free(device); 763} 764 765static SDL_PropertiesID METAL_GetDeviceProperties(SDL_GPUDevice *device) 766{ 767 MetalRenderer *renderer = (MetalRenderer *)device->driverData; 768 return renderer->props; 769} 770 771// Resource tracking 772 773static void METAL_INTERNAL_TrackBuffer( 774 MetalCommandBuffer *commandBuffer, 775 MetalBuffer *buffer) 776{ 777 TRACK_RESOURCE( 778 buffer, 779 MetalBuffer *, 780 usedBuffers, 781 usedBufferCount, 782 usedBufferCapacity); 783} 784 785static void METAL_INTERNAL_TrackTexture( 786 MetalCommandBuffer *commandBuffer, 787 MetalTexture *texture) 788{ 789 TRACK_RESOURCE( 790 texture, 791 MetalTexture *, 792 usedTextures, 793 usedTextureCount, 794 usedTextureCapacity); 795} 796 797static void METAL_INTERNAL_TrackUniformBuffer( 798 MetalCommandBuffer *commandBuffer, 799 MetalUniformBuffer *uniformBuffer) 800{ 801 Uint32 i; 802 for (i = 0; i < commandBuffer->usedUniformBufferCount; i += 1) { 803 if (commandBuffer->usedUniformBuffers[i] == uniformBuffer) { 804 return; 805 } 806 } 807 808 if (commandBuffer->usedUniformBufferCount == commandBuffer->usedUniformBufferCapacity) { 809 commandBuffer->usedUniformBufferCapacity += 1; 810 commandBuffer->usedUniformBuffers = SDL_realloc( 811 commandBuffer->usedUniformBuffers, 812 commandBuffer->usedUniformBufferCapacity * sizeof(MetalUniformBuffer *)); 813 } 814 815 commandBuffer->usedUniformBuffers[commandBuffer->usedUniformBufferCount] = uniformBuffer; 816 commandBuffer->usedUniformBufferCount += 1; 817} 818 819// Shader Compilation 820 821typedef struct MetalLibraryFunction 822{ 823 id<MTLLibrary> library; 824 id<MTLFunction> function; 825} MetalLibraryFunction; 826 827static bool METAL_INTERNAL_IsValidMetalLibrary( 828 const Uint8 *code, 829 size_t codeSize) 830{ 831 // Metal libraries have a 4 byte header containing `MTLB`. 832 if (codeSize < 4 || code == NULL) { 833 return false; 834 } 835 return SDL_memcmp(code, "MTLB", 4) == 0; 836} 837 838// This function assumes that it's called from within an autorelease pool 839static MetalLibraryFunction METAL_INTERNAL_CompileShader( 840 MetalRenderer *renderer, 841 SDL_GPUShaderFormat format, 842 const Uint8 *code, 843 size_t codeSize, 844 const char *entrypoint) 845{ 846 MetalLibraryFunction libraryFunction = { nil, nil }; 847 id<MTLLibrary> library; 848 NSError *error; 849 dispatch_data_t data; 850 id<MTLFunction> function; 851 852 if (!entrypoint) { 853 entrypoint = "main0"; 854 } 855 856 if (format == SDL_GPU_SHADERFORMAT_MSL) { 857 NSString *codeString = [[NSString alloc] 858 initWithBytes:code 859 length:codeSize 860 encoding:NSUTF8StringEncoding]; 861 library = [renderer->device 862 newLibraryWithSource:codeString 863 options:nil 864 error:&error]; 865 } else if (format == SDL_GPU_SHADERFORMAT_METALLIB) { 866 if (!METAL_INTERNAL_IsValidMetalLibrary(code, codeSize)) { 867 SET_STRING_ERROR_AND_RETURN( 868 "The provided shader code is not a valid Metal library!", 869 libraryFunction); 870 } 871 data = dispatch_data_create( 872 code, 873 codeSize, 874 dispatch_get_global_queue(0, 0), 875 DISPATCH_DATA_DESTRUCTOR_DEFAULT); 876 library = [renderer->device newLibraryWithData:data error:&error]; 877 } else { 878 SDL_assert(!"SDL_gpu.c should have already validated this!"); 879 return libraryFunction; 880 } 881 882 if (library == nil) { 883 SDL_LogError( 884 SDL_LOG_CATEGORY_GPU, 885 "Creating MTLLibrary failed: %s", 886 [[error description] cStringUsingEncoding:[NSString defaultCStringEncoding]]); 887 return libraryFunction; 888 } else if (error != nil) { 889 SDL_LogWarn( 890 SDL_LOG_CATEGORY_GPU, 891 "Creating MTLLibrary failed: %s", 892 [[error description] cStringUsingEncoding:[NSString defaultCStringEncoding]]); 893 } 894 895 function = [library newFunctionWithName:@(entrypoint)]; 896 if (function == nil) { 897 SDL_LogError( 898 SDL_LOG_CATEGORY_GPU, 899 "Creating MTLFunction failed"); 900 return libraryFunction; 901 } 902 903 libraryFunction.library = library; 904 libraryFunction.function = function; 905 return libraryFunction; 906} 907 908// Disposal 909 910static void METAL_INTERNAL_DestroyTextureContainer( 911 MetalTextureContainer *container) 912{ 913 for (Uint32 i = 0; i < container->textureCount; i += 1) { 914 container->textures[i]->handle = nil; 915 SDL_free(container->textures[i]); 916 } 917 SDL_DestroyProperties(container->header.info.props); 918 if (container->debugName != NULL) { 919 SDL_free(container->debugName); 920 } 921 SDL_free(container->textures); 922 SDL_free(container); 923} 924 925static void METAL_ReleaseTexture( 926 SDL_GPURenderer *driverData, 927 SDL_GPUTexture *texture) 928{ 929 MetalRenderer *renderer = (MetalRenderer *)driverData; 930 MetalTextureContainer *container = (MetalTextureContainer *)texture; 931 932 SDL_LockMutex(renderer->disposeLock); 933 934 EXPAND_ARRAY_IF_NEEDED( 935 renderer->textureContainersToDestroy, 936 MetalTextureContainer *, 937 renderer->textureContainersToDestroyCount + 1, 938 renderer->textureContainersToDestroyCapacity, 939 renderer->textureContainersToDestroyCapacity + 1); 940 941 renderer->textureContainersToDestroy[renderer->textureContainersToDestroyCount] = container; 942 renderer->textureContainersToDestroyCount += 1; 943 944 SDL_UnlockMutex(renderer->disposeLock); 945} 946 947static void METAL_ReleaseSampler( 948 SDL_GPURenderer *driverData, 949 SDL_GPUSampler *sampler) 950{ 951 @autoreleasepool { 952 MetalSampler *metalSampler = (MetalSampler *)sampler; 953 metalSampler->handle = nil; 954 SDL_free(metalSampler); 955 } 956} 957 958static void METAL_INTERNAL_DestroyBufferContainer( 959 MetalBufferContainer *container) 960{ 961 for (Uint32 i = 0; i < container->bufferCount; i += 1) { 962 container->buffers[i]->handle = nil; 963 SDL_free(container->buffers[i]); 964 } 965 if (container->debugName != NULL) { 966 SDL_free(container->debugName); 967 } 968 SDL_free(container->buffers); 969 SDL_free(container); 970} 971 972static void METAL_ReleaseBuffer( 973 SDL_GPURenderer *driverData, 974 SDL_GPUBuffer *buffer) 975{ 976 MetalRenderer *renderer = (MetalRenderer *)driverData; 977 MetalBufferContainer *container = (MetalBufferContainer *)buffer; 978 979 SDL_LockMutex(renderer->disposeLock); 980 981 EXPAND_ARRAY_IF_NEEDED( 982 renderer->bufferContainersToDestroy, 983 MetalBufferContainer *, 984 renderer->bufferContainersToDestroyCount + 1, 985 renderer->bufferContainersToDestroyCapacity, 986 renderer->bufferContainersToDestroyCapacity + 1); 987 988 renderer->bufferContainersToDestroy[renderer->bufferContainersToDestroyCount] = container; 989 renderer->bufferContainersToDestroyCount += 1; 990 991 SDL_UnlockMutex(renderer->disposeLock); 992} 993 994static void METAL_ReleaseTransferBuffer( 995 SDL_GPURenderer *driverData, 996 SDL_GPUTransferBuffer *transferBuffer) 997{ 998 METAL_ReleaseBuffer( 999 driverData, 1000 (SDL_GPUBuffer *)transferBuffer); 1001} 1002 1003static void METAL_ReleaseShader( 1004 SDL_GPURenderer *driverData, 1005 SDL_GPUShader *shader) 1006{ 1007 @autoreleasepool { 1008 MetalShader *metalShader = (MetalShader *)shader; 1009 metalShader->function = nil; 1010 metalShader->library = nil; 1011 SDL_free(metalShader); 1012 } 1013} 1014 1015static void METAL_ReleaseComputePipeline( 1016 SDL_GPURenderer *driverData, 1017 SDL_GPUComputePipeline *computePipeline) 1018{ 1019 @autoreleasepool { 1020 MetalComputePipeline *metalComputePipeline = (MetalComputePipeline *)computePipeline; 1021 metalComputePipeline->handle = nil; 1022 SDL_free(metalComputePipeline); 1023 } 1024} 1025 1026static void METAL_ReleaseGraphicsPipeline( 1027 SDL_GPURenderer *driverData, 1028 SDL_GPUGraphicsPipeline *graphicsPipeline) 1029{ 1030 @autoreleasepool { 1031 MetalGraphicsPipeline *metalGraphicsPipeline = (MetalGraphicsPipeline *)graphicsPipeline; 1032 metalGraphicsPipeline->handle = nil; 1033 metalGraphicsPipeline->depth_stencil_state = nil; 1034 SDL_free(metalGraphicsPipeline); 1035 } 1036} 1037 1038// Pipeline Creation 1039 1040static SDL_GPUComputePipeline *METAL_CreateComputePipeline( 1041 SDL_GPURenderer *driverData, 1042 const SDL_GPUComputePipelineCreateInfo *createinfo) 1043{ 1044 @autoreleasepool { 1045 MetalRenderer *renderer = (MetalRenderer *)driverData; 1046 MetalLibraryFunction libraryFunction; 1047 id<MTLComputePipelineState> handle; 1048 MetalComputePipeline *pipeline; 1049 NSError *error; 1050 1051 libraryFunction = METAL_INTERNAL_CompileShader( 1052 renderer, 1053 createinfo->format, 1054 createinfo->code, 1055 createinfo->code_size, 1056 createinfo->entrypoint); 1057 1058 if (libraryFunction.library == nil || libraryFunction.function == nil) { 1059 return NULL; 1060 } 1061 1062 MTLComputePipelineDescriptor *descriptor = [MTLComputePipelineDescriptor new]; 1063 descriptor.computeFunction = libraryFunction.function; 1064 1065 if (renderer->debugMode && SDL_HasProperty(createinfo->props, SDL_PROP_GPU_COMPUTEPIPELINE_CREATE_NAME_STRING)) { 1066 const char *name = SDL_GetStringProperty(createinfo->props, SDL_PROP_GPU_COMPUTEPIPELINE_CREATE_NAME_STRING, NULL); 1067 descriptor.label = @(name); 1068 } 1069 1070 handle = [renderer->device newComputePipelineStateWithDescriptor:descriptor options:MTLPipelineOptionNone reflection: nil error:&error]; 1071 if (error != NULL) { 1072 SET_ERROR_AND_RETURN("Creating compute pipeline failed: %s", [[error description] UTF8String], NULL); 1073 } 1074 1075 pipeline = SDL_calloc(1, sizeof(MetalComputePipeline)); 1076 pipeline->handle = handle; 1077 pipeline->header.numSamplers = createinfo->num_samplers; 1078 pipeline->header.numReadonlyStorageTextures = createinfo->num_readonly_storage_textures; 1079 pipeline->header.numReadWriteStorageTextures = createinfo->num_readwrite_storage_textures; 1080 pipeline->header.numReadonlyStorageBuffers = createinfo->num_readonly_storage_buffers; 1081 pipeline->header.numReadWriteStorageBuffers = createinfo->num_readwrite_storage_buffers; 1082 pipeline->header.numUniformBuffers = createinfo->num_uniform_buffers; 1083 pipeline->threadcountX = createinfo->threadcount_x; 1084 pipeline->threadcountY = createinfo->threadcount_y; 1085 pipeline->threadcountZ = createinfo->threadcount_z; 1086 1087 return (SDL_GPUComputePipeline *)pipeline; 1088 } 1089} 1090 1091static SDL_GPUGraphicsPipeline *METAL_CreateGraphicsPipeline( 1092 SDL_GPURenderer *driverData, 1093 const SDL_GPUGraphicsPipelineCreateInfo *createinfo) 1094{ 1095 @autoreleasepool { 1096 MetalRenderer *renderer = (MetalRenderer *)driverData; 1097 MetalShader *vertexShader = (MetalShader *)createinfo->vertex_shader; 1098 MetalShader *fragmentShader = (MetalShader *)createinfo->fragment_shader; 1099 MTLRenderPipelineDescriptor *pipelineDescriptor; 1100 const SDL_GPUColorTargetBlendState *blendState; 1101 MTLVertexDescriptor *vertexDescriptor; 1102 Uint32 binding; 1103 MTLDepthStencilDescriptor *depthStencilDescriptor; 1104 MTLStencilDescriptor *frontStencilDescriptor = NULL; 1105 MTLStencilDescriptor *backStencilDescriptor = NULL; 1106 id<MTLDepthStencilState> depthStencilState = nil; 1107 id<MTLRenderPipelineState> pipelineState = nil; 1108 NSError *error = NULL; 1109 MetalGraphicsPipeline *result = NULL; 1110 1111 if (renderer->debugMode) { 1112 if (vertexShader->stage != SDL_GPU_SHADERSTAGE_VERTEX) { 1113 SDL_assert_release(!"CreateGraphicsPipeline was passed a fragment shader for the vertex stage"); 1114 } 1115 if (fragmentShader->stage != SDL_GPU_SHADERSTAGE_FRAGMENT) { 1116 SDL_assert_release(!"CreateGraphicsPipeline was passed a vertex shader for the fragment stage"); 1117 } 1118 } 1119#ifdef SDL_PLATFORM_VISIONOS 1120 // The default is depth clipping enabled and it can't be changed 1121 if (!createinfo->rasterizer_state.enable_depth_clip) { 1122 SDL_assert_release(!"Rasterizer state enable_depth_clip must be true on this platform"); 1123 } 1124#endif 1125 1126 pipelineDescriptor = [MTLRenderPipelineDescriptor new]; 1127 1128 // Blend 1129 1130 for (Uint32 i = 0; i < createinfo->target_info.num_color_targets; i += 1) { 1131 blendState = &createinfo->target_info.color_target_descriptions[i].blend_state; 1132 SDL_GPUColorComponentFlags colorWriteMask = blendState->enable_color_write_mask ? 1133 blendState->color_write_mask : 1134 0xF; 1135 1136 pipelineDescriptor.colorAttachments[i].pixelFormat = SDLToMetal_TextureFormat(createinfo->target_info.color_target_descriptions[i].format); 1137 pipelineDescriptor.colorAttachments[i].writeMask = SDLToMetal_ColorWriteMask(colorWriteMask); 1138 pipelineDescriptor.colorAttachments[i].blendingEnabled = blendState->enable_blend; 1139 pipelineDescriptor.colorAttachments[i].rgbBlendOperation = SDLToMetal_BlendOp[blendState->color_blend_op]; 1140 pipelineDescriptor.colorAttachments[i].alphaBlendOperation = SDLToMetal_BlendOp[blendState->alpha_blend_op]; 1141 pipelineDescriptor.colorAttachments[i].sourceRGBBlendFactor = SDLToMetal_BlendFactor[blendState->src_color_blendfactor]; 1142 pipelineDescriptor.colorAttachments[i].sourceAlphaBlendFactor = SDLToMetal_BlendFactor[blendState->src_alpha_blendfactor]; 1143 pipelineDescriptor.colorAttachments[i].destinationRGBBlendFactor = SDLToMetal_BlendFactor[blendState->dst_color_blendfactor]; 1144 pipelineDescriptor.colorAttachments[i].destinationAlphaBlendFactor = SDLToMetal_BlendFactor[blendState->dst_alpha_blendfactor]; 1145 } 1146 1147 // Multisample 1148 1149 pipelineDescriptor.rasterSampleCount = SDLToMetal_SampleCount[createinfo->multisample_state.sample_count]; 1150 pipelineDescriptor.alphaToCoverageEnabled = createinfo->multisample_state.enable_alpha_to_coverage; 1151 1152 // Depth Stencil 1153 1154 if (createinfo->target_info.has_depth_stencil_target) { 1155 pipelineDescriptor.depthAttachmentPixelFormat = SDLToMetal_TextureFormat(createinfo->target_info.depth_stencil_format); 1156 if (IsStencilFormat(createinfo->target_info.depth_stencil_format)) { 1157 pipelineDescriptor.stencilAttachmentPixelFormat = SDLToMetal_TextureFormat(createinfo->target_info.depth_stencil_format); 1158 } 1159 1160 if (createinfo->depth_stencil_state.enable_stencil_test) { 1161 frontStencilDescriptor = [MTLStencilDescriptor new]; 1162 frontStencilDescriptor.stencilCompareFunction = SDLToMetal_CompareOp[createinfo->depth_stencil_state.front_stencil_state.compare_op]; 1163 frontStencilDescriptor.stencilFailureOperation = SDLToMetal_StencilOp[createinfo->depth_stencil_state.front_stencil_state.fail_op]; 1164 frontStencilDescriptor.depthStencilPassOperation = SDLToMetal_StencilOp[createinfo->depth_stencil_state.front_stencil_state.pass_op]; 1165 frontStencilDescriptor.depthFailureOperation = SDLToMetal_StencilOp[createinfo->depth_stencil_state.front_stencil_state.depth_fail_op]; 1166 frontStencilDescriptor.readMask = createinfo->depth_stencil_state.compare_mask; 1167 frontStencilDescriptor.writeMask = createinfo->depth_stencil_state.write_mask; 1168 1169 backStencilDescriptor = [MTLStencilDescriptor new]; 1170 backStencilDescriptor.stencilCompareFunction = SDLToMetal_CompareOp[createinfo->depth_stencil_state.back_stencil_state.compare_op]; 1171 backStencilDescriptor.stencilFailureOperation = SDLToMetal_StencilOp[createinfo->depth_stencil_state.back_stencil_state.fail_op]; 1172 backStencilDescriptor.depthStencilPassOperation = SDLToMetal_StencilOp[createinfo->depth_stencil_state.back_stencil_state.pass_op]; 1173 backStencilDescriptor.depthFailureOperation = SDLToMetal_StencilOp[createinfo->depth_stencil_state.back_stencil_state.depth_fail_op]; 1174 backStencilDescriptor.readMask = createinfo->depth_stencil_state.compare_mask; 1175 backStencilDescriptor.writeMask = createinfo->depth_stencil_state.write_mask; 1176 } 1177 1178 depthStencilDescriptor = [MTLDepthStencilDescriptor new]; 1179 depthStencilDescriptor.depthCompareFunction = createinfo->depth_stencil_state.enable_depth_test ? SDLToMetal_CompareOp[createinfo->depth_stencil_state.compare_op] : MTLCompareFunctionAlways; 1180 // Disable write when test is disabled, to match other APIs' behavior 1181 depthStencilDescriptor.depthWriteEnabled = createinfo->depth_stencil_state.enable_depth_write && createinfo->depth_stencil_state.enable_depth_test; 1182 depthStencilDescriptor.frontFaceStencil = frontStencilDescriptor; 1183 depthStencilDescriptor.backFaceStencil = backStencilDescriptor; 1184 1185 depthStencilState = [renderer->device newDepthStencilStateWithDescriptor:depthStencilDescriptor]; 1186 } 1187 1188 // Shaders 1189 1190 pipelineDescriptor.vertexFunction = vertexShader->function; 1191 pipelineDescriptor.fragmentFunction = fragmentShader->function; 1192 1193 // Vertex Descriptor 1194 1195 if (createinfo->vertex_input_state.num_vertex_buffers > 0) { 1196 vertexDescriptor = [MTLVertexDescriptor vertexDescriptor]; 1197 1198 for (Uint32 i = 0; i < createinfo->vertex_input_state.num_vertex_attributes; i += 1) { 1199 Uint32 loc = createinfo->vertex_input_state.vertex_attributes[i].location; 1200 vertexDescriptor.attributes[loc].format = SDLToMetal_VertexFormat[createinfo->vertex_input_state.vertex_attributes[i].format]; 1201 vertexDescriptor.attributes[loc].offset = createinfo->vertex_input_state.vertex_attributes[i].offset; 1202 vertexDescriptor.attributes[loc].bufferIndex = 1203 METAL_FIRST_VERTEX_BUFFER_SLOT + createinfo->vertex_input_state.vertex_attributes[i].buffer_slot; 1204 } 1205 1206 for (Uint32 i = 0; i < createinfo->vertex_input_state.num_vertex_buffers; i += 1) { 1207 binding = METAL_FIRST_VERTEX_BUFFER_SLOT + createinfo->vertex_input_state.vertex_buffer_descriptions[i].slot; 1208 vertexDescriptor.layouts[binding].stepFunction = SDLToMetal_StepFunction[createinfo->vertex_input_state.vertex_buffer_descriptions[i].input_rate]; 1209 vertexDescriptor.layouts[binding].stepRate = 1; 1210 vertexDescriptor.layouts[binding].stride = createinfo->vertex_input_state.vertex_buffer_descriptions[i].pitch; 1211 } 1212 1213 pipelineDescriptor.vertexDescriptor = vertexDescriptor; 1214 } 1215 1216 if (renderer->debugMode && SDL_HasProperty(createinfo->props, SDL_PROP_GPU_GRAPHICSPIPELINE_CREATE_NAME_STRING)) { 1217 const char *name = SDL_GetStringProperty(createinfo->props, SDL_PROP_GPU_GRAPHICSPIPELINE_CREATE_NAME_STRING, NULL); 1218 pipelineDescriptor.label = @(name); 1219 } 1220 1221 // Create the graphics pipeline 1222 1223 pipelineState = [renderer->device newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error]; 1224 if (error != NULL) { 1225 SET_ERROR_AND_RETURN("Creating render pipeline failed: %s", [[error description] UTF8String], NULL); 1226 } 1227 1228 result = SDL_calloc(1, sizeof(MetalGraphicsPipeline)); 1229 result->handle = pipelineState; 1230 result->depth_stencil_state = depthStencilState; 1231 result->rasterizerState = createinfo->rasterizer_state; 1232 result->primitiveType = createinfo->primitive_type; 1233 result->header.num_vertex_samplers = vertexShader->numSamplers; 1234 result->header.num_vertex_uniform_buffers = vertexShader->numUniformBuffers; 1235 result->header.num_vertex_storage_buffers = vertexShader->numStorageBuffers; 1236 result->header.num_vertex_storage_textures = vertexShader->numStorageTextures; 1237 result->header.num_fragment_samplers = fragmentShader->numSamplers; 1238 result->header.num_fragment_uniform_buffers = fragmentShader->numUniformBuffers; 1239 result->header.num_fragment_storage_buffers = fragmentShader->numStorageBuffers; 1240 result->header.num_fragment_storage_textures = fragmentShader->numStorageTextures; 1241 return (SDL_GPUGraphicsPipeline *)result; 1242 } 1243} 1244 1245// Debug Naming 1246 1247static void METAL_SetBufferName( 1248 SDL_GPURenderer *driverData, 1249 SDL_GPUBuffer *buffer, 1250 const char *text) 1251{ 1252 @autoreleasepool { 1253 MetalRenderer *renderer = (MetalRenderer *)driverData; 1254 MetalBufferContainer *container = (MetalBufferContainer *)buffer; 1255 1256 if (renderer->debugMode && text != NULL) { 1257 if (container->debugName != NULL) { 1258 SDL_free(container->debugName); 1259 } 1260 1261 container->debugName = SDL_strdup(text); 1262 1263 for (Uint32 i = 0; i < container->bufferCount; i += 1) { 1264 container->buffers[i]->handle.label = @(text); 1265 } 1266 } 1267 } 1268} 1269 1270static void METAL_SetTextureName( 1271 SDL_GPURenderer *driverData, 1272 SDL_GPUTexture *texture, 1273 const char *text) 1274{ 1275 @autoreleasepool { 1276 MetalRenderer *renderer = (MetalRenderer *)driverData; 1277 MetalTextureContainer *container = (MetalTextureContainer *)texture; 1278 1279 if (renderer->debugMode && text != NULL) { 1280 if (container->debugName != NULL) { 1281 SDL_free(container->debugName); 1282 } 1283 1284 container->debugName = SDL_strdup(text); 1285 1286 for (Uint32 i = 0; i < container->textureCount; i += 1) { 1287 container->textures[i]->handle.label = @(text); 1288 } 1289 } 1290 } 1291} 1292 1293static void METAL_InsertDebugLabel( 1294 SDL_GPUCommandBuffer *commandBuffer, 1295 const char *text) 1296{ 1297 @autoreleasepool { 1298 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 1299 NSString *label = @(text); 1300 1301 if (metalCommandBuffer->renderEncoder) { 1302 [metalCommandBuffer->renderEncoder insertDebugSignpost:label]; 1303 } else if (metalCommandBuffer->blitEncoder) { 1304 [metalCommandBuffer->blitEncoder insertDebugSignpost:label]; 1305 } else if (metalCommandBuffer->computeEncoder) { 1306 [metalCommandBuffer->computeEncoder insertDebugSignpost:label]; 1307 } else { 1308 // Metal doesn't have insertDebugSignpost for command buffers... 1309 [metalCommandBuffer->handle pushDebugGroup:label]; 1310 [metalCommandBuffer->handle popDebugGroup]; 1311 } 1312 } 1313} 1314 1315static void METAL_PushDebugGroup( 1316 SDL_GPUCommandBuffer *commandBuffer, 1317 const char *name) 1318{ 1319 @autoreleasepool { 1320 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 1321 NSString *label = @(name); 1322 1323 if (metalCommandBuffer->renderEncoder) { 1324 [metalCommandBuffer->renderEncoder pushDebugGroup:label]; 1325 } else if (metalCommandBuffer->blitEncoder) { 1326 [metalCommandBuffer->blitEncoder pushDebugGroup:label]; 1327 } else if (metalCommandBuffer->computeEncoder) { 1328 [metalCommandBuffer->computeEncoder pushDebugGroup:label]; 1329 } else { 1330 [metalCommandBuffer->handle pushDebugGroup:label]; 1331 } 1332 } 1333} 1334 1335static void METAL_PopDebugGroup( 1336 SDL_GPUCommandBuffer *commandBuffer) 1337{ 1338 @autoreleasepool { 1339 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 1340 1341 if (metalCommandBuffer->renderEncoder) { 1342 [metalCommandBuffer->renderEncoder popDebugGroup]; 1343 } else if (metalCommandBuffer->blitEncoder) { 1344 [metalCommandBuffer->blitEncoder popDebugGroup]; 1345 } else if (metalCommandBuffer->computeEncoder) { 1346 [metalCommandBuffer->computeEncoder popDebugGroup]; 1347 } else { 1348 [metalCommandBuffer->handle popDebugGroup]; 1349 } 1350 } 1351} 1352 1353// Resource Creation 1354 1355static SDL_GPUSampler *METAL_CreateSampler( 1356 SDL_GPURenderer *driverData, 1357 const SDL_GPUSamplerCreateInfo *createinfo) 1358{ 1359 @autoreleasepool { 1360 MetalRenderer *renderer = (MetalRenderer *)driverData; 1361 MTLSamplerDescriptor *samplerDesc = [MTLSamplerDescriptor new]; 1362 id<MTLSamplerState> sampler; 1363 MetalSampler *metalSampler; 1364 1365 samplerDesc.sAddressMode = SDLToMetal_SamplerAddressMode[createinfo->address_mode_u]; 1366 samplerDesc.tAddressMode = SDLToMetal_SamplerAddressMode[createinfo->address_mode_v]; 1367 samplerDesc.rAddressMode = SDLToMetal_SamplerAddressMode[createinfo->address_mode_w]; 1368 samplerDesc.minFilter = SDLToMetal_MinMagFilter[createinfo->min_filter]; 1369 samplerDesc.magFilter = SDLToMetal_MinMagFilter[createinfo->mag_filter]; 1370 samplerDesc.mipFilter = SDLToMetal_MipFilter[createinfo->mipmap_mode]; // FIXME: Is this right with non-mipmapped samplers? 1371 samplerDesc.lodMinClamp = createinfo->min_lod; 1372 samplerDesc.lodMaxClamp = createinfo->max_lod; 1373 samplerDesc.maxAnisotropy = (NSUInteger)((createinfo->enable_anisotropy) ? createinfo->max_anisotropy : 1); 1374 samplerDesc.compareFunction = (createinfo->enable_compare) ? SDLToMetal_CompareOp[createinfo->compare_op] : MTLCompareFunctionAlways; 1375 1376 if (renderer->debugMode && SDL_HasProperty(createinfo->props, SDL_PROP_GPU_SAMPLER_CREATE_NAME_STRING)) { 1377 const char *name = SDL_GetStringProperty(createinfo->props, SDL_PROP_GPU_SAMPLER_CREATE_NAME_STRING, NULL); 1378 samplerDesc.label = @(name); 1379 } 1380 1381 sampler = [renderer->device newSamplerStateWithDescriptor:samplerDesc]; 1382 if (sampler == NULL) { 1383 SET_STRING_ERROR_AND_RETURN("Failed to create sampler", NULL); 1384 } 1385 1386 metalSampler = (MetalSampler *)SDL_calloc(1, sizeof(MetalSampler)); 1387 metalSampler->handle = sampler; 1388 return (SDL_GPUSampler *)metalSampler; 1389 } 1390} 1391 1392static SDL_GPUShader *METAL_CreateShader( 1393 SDL_GPURenderer *driverData, 1394 const SDL_GPUShaderCreateInfo *createinfo) 1395{ 1396 @autoreleasepool { 1397 MetalLibraryFunction libraryFunction; 1398 MetalShader *result; 1399 1400 libraryFunction = METAL_INTERNAL_CompileShader( 1401 (MetalRenderer *)driverData, 1402 createinfo->format, 1403 createinfo->code, 1404 createinfo->code_size, 1405 createinfo->entrypoint); 1406 1407 if (libraryFunction.library == nil || libraryFunction.function == nil) { 1408 return NULL; 1409 } 1410 1411 result = SDL_calloc(1, sizeof(MetalShader)); 1412 result->library = libraryFunction.library; 1413 result->function = libraryFunction.function; 1414 result->stage = createinfo->stage; 1415 result->numSamplers = createinfo->num_samplers; 1416 result->numStorageBuffers = createinfo->num_storage_buffers; 1417 result->numStorageTextures = createinfo->num_storage_textures; 1418 result->numUniformBuffers = createinfo->num_uniform_buffers; 1419 return (SDL_GPUShader *)result; 1420 } 1421} 1422 1423// This function assumes that it's called from within an autorelease pool 1424static MetalTexture *METAL_INTERNAL_CreateTexture( 1425 MetalRenderer *renderer, 1426 const SDL_GPUTextureCreateInfo *createinfo) 1427{ 1428 MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor new]; 1429 id<MTLTexture> texture; 1430 MetalTexture *metalTexture; 1431 1432 textureDescriptor.textureType = SDLToMetal_TextureType(createinfo->type, createinfo->sample_count > SDL_GPU_SAMPLECOUNT_1); 1433 textureDescriptor.pixelFormat = SDLToMetal_TextureFormat(createinfo->format); 1434 // This format isn't natively supported so let's swizzle! 1435 if (createinfo->format == SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM) { 1436 if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) { 1437 textureDescriptor.swizzle = MTLTextureSwizzleChannelsMake(MTLTextureSwizzleBlue, 1438 MTLTextureSwizzleGreen, 1439 MTLTextureSwizzleRed, 1440 MTLTextureSwizzleAlpha); 1441 } else { 1442 SET_STRING_ERROR_AND_RETURN("SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM is not supported", NULL); 1443 } 1444 } 1445 1446 textureDescriptor.width = createinfo->width; 1447 textureDescriptor.height = createinfo->height; 1448 textureDescriptor.depth = (createinfo->type == SDL_GPU_TEXTURETYPE_3D) ? createinfo->layer_count_or_depth : 1; 1449 textureDescriptor.mipmapLevelCount = createinfo->num_levels; 1450 textureDescriptor.sampleCount = SDLToMetal_SampleCount[createinfo->sample_count]; 1451 textureDescriptor.arrayLength = 1452 (createinfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY || createinfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) 1453 ? createinfo->layer_count_or_depth 1454 : 1; 1455 textureDescriptor.storageMode = MTLStorageModePrivate; 1456 1457 textureDescriptor.usage = 0; 1458 if (createinfo->usage & (SDL_GPU_TEXTUREUSAGE_COLOR_TARGET | 1459 SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET)) { 1460 textureDescriptor.usage |= MTLTextureUsageRenderTarget; 1461 } 1462 if (createinfo->usage & (SDL_GPU_TEXTUREUSAGE_SAMPLER | 1463 SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ | 1464 SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ)) { 1465 textureDescriptor.usage |= MTLTextureUsageShaderRead; 1466 } 1467 if (createinfo->usage & (SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE | 1468 SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE)) { 1469 textureDescriptor.usage |= MTLTextureUsageShaderWrite; 1470 } 1471 1472 texture = [renderer->device newTextureWithDescriptor:textureDescriptor]; 1473 if (texture == NULL) { 1474 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create MTLTexture!"); 1475 return NULL; 1476 } 1477 1478 metalTexture = (MetalTexture *)SDL_calloc(1, sizeof(MetalTexture)); 1479 metalTexture->handle = texture; 1480 SDL_SetAtomicInt(&metalTexture->referenceCount, 0); 1481 1482 if (renderer->debugMode && SDL_HasProperty(createinfo->props, SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING)) { 1483 metalTexture->handle.label = @(SDL_GetStringProperty(createinfo->props, SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING, NULL)); 1484 } 1485 1486 return metalTexture; 1487} 1488 1489static bool METAL_SupportsSampleCount( 1490 SDL_GPURenderer *driverData, 1491 SDL_GPUTextureFormat format, 1492 SDL_GPUSampleCount sampleCount) 1493{ 1494 @autoreleasepool { 1495 MetalRenderer *renderer = (MetalRenderer *)driverData; 1496 NSUInteger mtlSampleCount = SDLToMetal_SampleCount[sampleCount]; 1497 return [renderer->device supportsTextureSampleCount:mtlSampleCount]; 1498 } 1499} 1500 1501static SDL_GPUTexture *METAL_CreateTexture( 1502 SDL_GPURenderer *driverData, 1503 const SDL_GPUTextureCreateInfo *createinfo) 1504{ 1505 @autoreleasepool { 1506 MetalRenderer *renderer = (MetalRenderer *)driverData; 1507 MetalTextureContainer *container; 1508 MetalTexture *texture; 1509 1510 texture = METAL_INTERNAL_CreateTexture( 1511 renderer, 1512 createinfo); 1513 1514 if (texture == NULL) { 1515 SET_STRING_ERROR_AND_RETURN("Failed to create texture", NULL); 1516 } 1517 1518 container = SDL_calloc(1, sizeof(MetalTextureContainer)); 1519 container->canBeCycled = 1; 1520 1521 // Copy properties so we don't lose information when the client destroys them 1522 container->header.info = *createinfo; 1523 container->header.info.props = SDL_CreateProperties(); 1524 if (createinfo->props) { 1525 SDL_CopyProperties(createinfo->props, container->header.info.props); 1526 } 1527 1528 container->activeTexture = texture; 1529 container->textureCapacity = 1; 1530 container->textureCount = 1; 1531 container->textures = SDL_calloc( 1532 container->textureCapacity, sizeof(MetalTexture *)); 1533 container->textures[0] = texture; 1534 container->debugName = NULL; 1535 1536 if (SDL_HasProperty(createinfo->props, SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING)) { 1537 container->debugName = SDL_strdup(SDL_GetStringProperty(createinfo->props, SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING, NULL)); 1538 } 1539 1540 return (SDL_GPUTexture *)container; 1541 } 1542} 1543 1544// This function assumes that it's called from within an autorelease pool 1545static MetalTexture *METAL_INTERNAL_PrepareTextureForWrite( 1546 MetalRenderer *renderer, 1547 MetalTextureContainer *container, 1548 bool cycle) 1549{ 1550 Uint32 i; 1551 1552 // Cycle the active texture handle if needed 1553 if (cycle && container->canBeCycled) { 1554 for (i = 0; i < container->textureCount; i += 1) { 1555 if (SDL_GetAtomicInt(&container->textures[i]->referenceCount) == 0) { 1556 container->activeTexture = container->textures[i]; 1557 return container->activeTexture; 1558 } 1559 } 1560 1561 EXPAND_ARRAY_IF_NEEDED( 1562 container->textures, 1563 MetalTexture *, 1564 container->textureCount + 1, 1565 container->textureCapacity, 1566 container->textureCapacity + 1); 1567 1568 container->textures[container->textureCount] = METAL_INTERNAL_CreateTexture( 1569 renderer, 1570 &container->header.info); 1571 container->textureCount += 1; 1572 1573 container->activeTexture = container->textures[container->textureCount - 1]; 1574 } 1575 1576 return container->activeTexture; 1577} 1578 1579// This function assumes that it's called from within an autorelease pool 1580static MetalBuffer *METAL_INTERNAL_CreateBuffer( 1581 MetalRenderer *renderer, 1582 Uint32 size, 1583 MTLResourceOptions resourceOptions, 1584 const char *debugName) 1585{ 1586 id<MTLBuffer> bufferHandle; 1587 MetalBuffer *metalBuffer; 1588 1589 // Storage buffers have to be 4-aligned, so might as well align them all 1590 size = METAL_INTERNAL_NextHighestAlignment(size, 4); 1591 1592 bufferHandle = [renderer->device newBufferWithLength:size options:resourceOptions]; 1593 if (bufferHandle == NULL) { 1594 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Could not create buffer"); 1595 return NULL; 1596 } 1597 1598 metalBuffer = SDL_calloc(1, sizeof(MetalBuffer)); 1599 metalBuffer->handle = bufferHandle; 1600 SDL_SetAtomicInt(&metalBuffer->referenceCount, 0); 1601 1602 if (debugName != NULL) { 1603 metalBuffer->handle.label = @(debugName); 1604 } 1605 1606 return metalBuffer; 1607} 1608 1609// This function assumes that it's called from within an autorelease pool 1610static MetalBufferContainer *METAL_INTERNAL_CreateBufferContainer( 1611 MetalRenderer *renderer, 1612 Uint32 size, 1613 bool isPrivate, 1614 bool isWriteOnly, 1615 const char *debugName) 1616{ 1617 MetalBufferContainer *container = SDL_calloc(1, sizeof(MetalBufferContainer)); 1618 MTLResourceOptions resourceOptions; 1619 1620 container->size = size; 1621 container->bufferCapacity = 1; 1622 container->bufferCount = 1; 1623 container->buffers = SDL_calloc( 1624 container->bufferCapacity, sizeof(MetalBuffer *)); 1625 container->isPrivate = isPrivate; 1626 container->isWriteOnly = isWriteOnly; 1627 container->debugName = NULL; 1628 if (container->debugName != NULL) { 1629 container->debugName = SDL_strdup(debugName); 1630 } 1631 1632 if (isPrivate) { 1633 resourceOptions = MTLResourceStorageModePrivate; 1634 } else { 1635 if (isWriteOnly) { 1636 resourceOptions = MTLResourceCPUCacheModeWriteCombined; 1637 } else { 1638 resourceOptions = MTLResourceCPUCacheModeDefaultCache; 1639 } 1640 } 1641 1642 container->buffers[0] = METAL_INTERNAL_CreateBuffer( 1643 renderer, 1644 size, 1645 resourceOptions, 1646 debugName); 1647 1648 container->activeBuffer = container->buffers[0]; 1649 1650 return container; 1651} 1652 1653static SDL_GPUBuffer *METAL_CreateBuffer( 1654 SDL_GPURenderer *driverData, 1655 SDL_GPUBufferUsageFlags usage, 1656 Uint32 size, 1657 const char *debugName) 1658{ 1659 @autoreleasepool { 1660 return (SDL_GPUBuffer *)METAL_INTERNAL_CreateBufferContainer( 1661 (MetalRenderer *)driverData, 1662 size, 1663 true, 1664 false, 1665 debugName); 1666 } 1667} 1668 1669static SDL_GPUTransferBuffer *METAL_CreateTransferBuffer( 1670 SDL_GPURenderer *driverData, 1671 SDL_GPUTransferBufferUsage usage, 1672 Uint32 size, 1673 const char *debugName) 1674{ 1675 @autoreleasepool { 1676 return (SDL_GPUTransferBuffer *)METAL_INTERNAL_CreateBufferContainer( 1677 (MetalRenderer *)driverData, 1678 size, 1679 false, 1680 usage == SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, 1681 debugName); 1682 } 1683} 1684 1685// This function assumes that it's called from within an autorelease pool 1686static MetalUniformBuffer *METAL_INTERNAL_CreateUniformBuffer( 1687 MetalRenderer *renderer, 1688 Uint32 size) 1689{ 1690 MetalUniformBuffer *uniformBuffer; 1691 id<MTLBuffer> bufferHandle; 1692 1693 bufferHandle = [renderer->device newBufferWithLength:size options:MTLResourceCPUCacheModeWriteCombined]; 1694 if (bufferHandle == nil) { 1695 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Could not create uniform buffer"); 1696 return NULL; 1697 } 1698 1699 uniformBuffer = SDL_calloc(1, sizeof(MetalUniformBuffer)); 1700 uniformBuffer->handle = bufferHandle; 1701 uniformBuffer->writeOffset = 0; 1702 uniformBuffer->drawOffset = 0; 1703 1704 return uniformBuffer; 1705} 1706 1707// This function assumes that it's called from within an autorelease pool 1708static MetalBuffer *METAL_INTERNAL_PrepareBufferForWrite( 1709 MetalRenderer *renderer, 1710 MetalBufferContainer *container, 1711 bool cycle) 1712{ 1713 MTLResourceOptions resourceOptions; 1714 Uint32 i; 1715 1716 // Cycle if needed 1717 if (cycle && SDL_GetAtomicInt(&container->activeBuffer->referenceCount) > 0) { 1718 for (i = 0; i < container->bufferCount; i += 1) { 1719 if (SDL_GetAtomicInt(&container->buffers[i]->referenceCount) == 0) { 1720 container->activeBuffer = container->buffers[i]; 1721 return container->activeBuffer; 1722 } 1723 } 1724 1725 EXPAND_ARRAY_IF_NEEDED( 1726 container->buffers, 1727 MetalBuffer *, 1728 container->bufferCount + 1, 1729 container->bufferCapacity, 1730 container->bufferCapacity + 1); 1731 1732 if (container->isPrivate) { 1733 resourceOptions = MTLResourceStorageModePrivate; 1734 } else { 1735 if (container->isWriteOnly) { 1736 resourceOptions = MTLResourceCPUCacheModeWriteCombined; 1737 } else { 1738 resourceOptions = MTLResourceCPUCacheModeDefaultCache; 1739 } 1740 } 1741 1742 container->buffers[container->bufferCount] = METAL_INTERNAL_CreateBuffer( 1743 renderer, 1744 container->size, 1745 resourceOptions, 1746 container->debugName); 1747 container->bufferCount += 1; 1748 1749 container->activeBuffer = container->buffers[container->bufferCount - 1]; 1750 } 1751 1752 return container->activeBuffer; 1753} 1754 1755// TransferBuffer Data 1756 1757static void *METAL_MapTransferBuffer( 1758 SDL_GPURenderer *driverData, 1759 SDL_GPUTransferBuffer *transferBuffer, 1760 bool cycle) 1761{ 1762 @autoreleasepool { 1763 MetalRenderer *renderer = (MetalRenderer *)driverData; 1764 MetalBufferContainer *container = (MetalBufferContainer *)transferBuffer; 1765 MetalBuffer *buffer = METAL_INTERNAL_PrepareBufferForWrite(renderer, container, cycle); 1766 return [buffer->handle contents]; 1767 } 1768} 1769 1770static void METAL_UnmapTransferBuffer( 1771 SDL_GPURenderer *driverData, 1772 SDL_GPUTransferBuffer *transferBuffer) 1773{ 1774#ifdef SDL_PLATFORM_MACOS 1775 @autoreleasepool { 1776 // FIXME: Is this necessary? 1777 MetalBufferContainer *container = (MetalBufferContainer *)transferBuffer; 1778 MetalBuffer *buffer = container->activeBuffer; 1779 if (buffer->handle.storageMode == MTLStorageModeManaged) { 1780 [buffer->handle didModifyRange:NSMakeRange(0, container->size)]; 1781 } 1782 } 1783#endif 1784} 1785 1786// Copy Pass 1787 1788static void METAL_BeginCopyPass( 1789 SDL_GPUCommandBuffer *commandBuffer) 1790{ 1791 @autoreleasepool { 1792 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 1793 metalCommandBuffer->blitEncoder = [metalCommandBuffer->handle blitCommandEncoder]; 1794 } 1795} 1796 1797static void METAL_UploadToTexture( 1798 SDL_GPUCommandBuffer *commandBuffer, 1799 const SDL_GPUTextureTransferInfo *source, 1800 const SDL_GPUTextureRegion *destination, 1801 bool cycle) 1802{ 1803 @autoreleasepool { 1804 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 1805 MetalRenderer *renderer = metalCommandBuffer->renderer; 1806 MetalBufferContainer *bufferContainer = (MetalBufferContainer *)source->transfer_buffer; 1807 MetalTextureContainer *textureContainer = (MetalTextureContainer *)destination->texture; 1808 1809 MetalTexture *metalTexture = METAL_INTERNAL_PrepareTextureForWrite(renderer, textureContainer, cycle); 1810 1811 [metalCommandBuffer->blitEncoder 1812 copyFromBuffer:bufferContainer->activeBuffer->handle 1813 sourceOffset:source->offset 1814 sourceBytesPerRow:BytesPerRow(destination->w, textureContainer->header.info.format) 1815 // sourceBytesPerImage expects the stride between 2D images (slices) of a 3D texture, not the size of the entire region 1816 sourceBytesPerImage:SDL_CalculateGPUTextureFormatSize(textureContainer->header.info.format, destination->w, destination->h, 1) 1817 sourceSize:MTLSizeMake(destination->w, destination->h, destination->d) 1818 toTexture:metalTexture->handle 1819 destinationSlice:destination->layer 1820 destinationLevel:destination->mip_level 1821 destinationOrigin:MTLOriginMake(destination->x, destination->y, destination->z)]; 1822 1823 METAL_INTERNAL_TrackTexture(metalCommandBuffer, metalTexture); 1824 METAL_INTERNAL_TrackBuffer(metalCommandBuffer, bufferContainer->activeBuffer); 1825 } 1826} 1827 1828static void METAL_UploadToBuffer( 1829 SDL_GPUCommandBuffer *commandBuffer, 1830 const SDL_GPUTransferBufferLocation *source, 1831 const SDL_GPUBufferRegion *destination, 1832 bool cycle) 1833{ 1834 @autoreleasepool { 1835 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 1836 MetalRenderer *renderer = metalCommandBuffer->renderer; 1837 MetalBufferContainer *transferContainer = (MetalBufferContainer *)source->transfer_buffer; 1838 MetalBufferContainer *bufferContainer = (MetalBufferContainer *)destination->buffer; 1839 1840 MetalBuffer *metalBuffer = METAL_INTERNAL_PrepareBufferForWrite( 1841 renderer, 1842 bufferContainer, 1843 cycle); 1844 1845 [metalCommandBuffer->blitEncoder 1846 copyFromBuffer:transferContainer->activeBuffer->handle 1847 sourceOffset:source->offset 1848 toBuffer:metalBuffer->handle 1849 destinationOffset:destination->offset 1850 size:destination->size]; 1851 1852 METAL_INTERNAL_TrackBuffer(metalCommandBuffer, metalBuffer); 1853 METAL_INTERNAL_TrackBuffer(metalCommandBuffer, transferContainer->activeBuffer); 1854 } 1855} 1856 1857static void METAL_CopyTextureToTexture( 1858 SDL_GPUCommandBuffer *commandBuffer, 1859 const SDL_GPUTextureLocation *source, 1860 const SDL_GPUTextureLocation *destination, 1861 Uint32 w, 1862 Uint32 h, 1863 Uint32 d, 1864 bool cycle) 1865{ 1866 @autoreleasepool { 1867 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 1868 MetalRenderer *renderer = metalCommandBuffer->renderer; 1869 MetalTextureContainer *srcContainer = (MetalTextureContainer *)source->texture; 1870 MetalTextureContainer *dstContainer = (MetalTextureContainer *)destination->texture; 1871 1872 MetalTexture *srcTexture = srcContainer->activeTexture; 1873 MetalTexture *dstTexture = METAL_INTERNAL_PrepareTextureForWrite( 1874 renderer, 1875 dstContainer, 1876 cycle); 1877 1878 [metalCommandBuffer->blitEncoder 1879 copyFromTexture:srcTexture->handle 1880 sourceSlice:source->layer 1881 sourceLevel:source->mip_level 1882 sourceOrigin:MTLOriginMake(source->x, source->y, source->z) 1883 sourceSize:MTLSizeMake(w, h, d) 1884 toTexture:dstTexture->handle 1885 destinationSlice:destination->layer 1886 destinationLevel:destination->mip_level 1887 destinationOrigin:MTLOriginMake(destination->x, destination->y, destination->z)]; 1888 1889 METAL_INTERNAL_TrackTexture(metalCommandBuffer, srcTexture); 1890 METAL_INTERNAL_TrackTexture(metalCommandBuffer, dstTexture); 1891 } 1892} 1893 1894static void METAL_CopyBufferToBuffer( 1895 SDL_GPUCommandBuffer *commandBuffer, 1896 const SDL_GPUBufferLocation *source, 1897 const SDL_GPUBufferLocation *destination, 1898 Uint32 size, 1899 bool cycle) 1900{ 1901 @autoreleasepool { 1902 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 1903 MetalRenderer *renderer = metalCommandBuffer->renderer; 1904 MetalBufferContainer *srcContainer = (MetalBufferContainer *)source->buffer; 1905 MetalBufferContainer *dstContainer = (MetalBufferContainer *)destination->buffer; 1906 1907 MetalBuffer *srcBuffer = srcContainer->activeBuffer; 1908 MetalBuffer *dstBuffer = METAL_INTERNAL_PrepareBufferForWrite( 1909 renderer, 1910 dstContainer, 1911 cycle); 1912 1913 [metalCommandBuffer->blitEncoder 1914 copyFromBuffer:srcBuffer->handle 1915 sourceOffset:source->offset 1916 toBuffer:dstBuffer->handle 1917 destinationOffset:destination->offset 1918 size:size]; 1919 1920 METAL_INTERNAL_TrackBuffer(metalCommandBuffer, srcBuffer); 1921 METAL_INTERNAL_TrackBuffer(metalCommandBuffer, dstBuffer); 1922 } 1923} 1924 1925static void METAL_DownloadFromTexture( 1926 SDL_GPUCommandBuffer *commandBuffer, 1927 const SDL_GPUTextureRegion *source, 1928 const SDL_GPUTextureTransferInfo *destination) 1929{ 1930 @autoreleasepool { 1931 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 1932 MetalRenderer *renderer = metalCommandBuffer->renderer; 1933 MetalTextureContainer *textureContainer = (MetalTextureContainer *)source->texture; 1934 MetalTexture *metalTexture = textureContainer->activeTexture; 1935 MetalBufferContainer *bufferContainer = (MetalBufferContainer *)destination->transfer_buffer; 1936 Uint32 bufferStride = destination->pixels_per_row; 1937 Uint32 bufferImageHeight = destination->rows_per_layer; 1938 Uint32 bytesPerRow, bytesPerDepthSlice; 1939 1940 MetalBuffer *dstBuffer = METAL_INTERNAL_PrepareBufferForWrite( 1941 renderer, 1942 bufferContainer, 1943 false); 1944 1945 MTLOrigin regionOrigin = MTLOriginMake( 1946 source->x, 1947 source->y, 1948 source->z); 1949 1950 MTLSize regionSize = MTLSizeMake( 1951 source->w, 1952 source->h, 1953 source->d); 1954 1955 if (bufferStride == 0 || bufferImageHeight == 0) { 1956 bufferStride = source->w; 1957 bufferImageHeight = source->h; 1958 } 1959 1960 bytesPerRow = BytesPerRow(bufferStride, textureContainer->header.info.format); 1961 bytesPerDepthSlice = bytesPerRow * bufferImageHeight; 1962 1963 [metalCommandBuffer->blitEncoder 1964 copyFromTexture:metalTexture->handle 1965 sourceSlice:source->layer 1966 sourceLevel:source->mip_level 1967 sourceOrigin:regionOrigin 1968 sourceSize:regionSize 1969 toBuffer:dstBuffer->handle 1970 destinationOffset:destination->offset 1971 destinationBytesPerRow:bytesPerRow 1972 destinationBytesPerImage:bytesPerDepthSlice]; 1973 1974 METAL_INTERNAL_TrackTexture(metalCommandBuffer, metalTexture); 1975 METAL_INTERNAL_TrackBuffer(metalCommandBuffer, dstBuffer); 1976 } 1977} 1978 1979static void METAL_DownloadFromBuffer( 1980 SDL_GPUCommandBuffer *commandBuffer, 1981 const SDL_GPUBufferRegion *source, 1982 const SDL_GPUTransferBufferLocation *destination) 1983{ 1984 SDL_GPUBufferLocation sourceLocation; 1985 sourceLocation.buffer = source->buffer; 1986 sourceLocation.offset = source->offset; 1987 1988 METAL_CopyBufferToBuffer( 1989 commandBuffer, 1990 &sourceLocation, 1991 (SDL_GPUBufferLocation *)destination, 1992 source->size, 1993 false); 1994} 1995 1996static void METAL_EndCopyPass( 1997 SDL_GPUCommandBuffer *commandBuffer) 1998{ 1999 @autoreleasepool { 2000 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2001 [metalCommandBuffer->blitEncoder endEncoding]; 2002 metalCommandBuffer->blitEncoder = nil; 2003 } 2004} 2005 2006static void METAL_GenerateMipmaps( 2007 SDL_GPUCommandBuffer *commandBuffer, 2008 SDL_GPUTexture *texture) 2009{ 2010 @autoreleasepool { 2011 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2012 MetalTextureContainer *container = (MetalTextureContainer *)texture; 2013 MetalTexture *metalTexture = container->activeTexture; 2014 2015 METAL_BeginCopyPass(commandBuffer); 2016 [metalCommandBuffer->blitEncoder 2017 generateMipmapsForTexture:metalTexture->handle]; 2018 METAL_EndCopyPass(commandBuffer); 2019 2020 METAL_INTERNAL_TrackTexture(metalCommandBuffer, metalTexture); 2021 } 2022} 2023 2024// Graphics State 2025 2026static void METAL_INTERNAL_AllocateCommandBuffers( 2027 MetalRenderer *renderer, 2028 Uint32 allocateCount) 2029{ 2030 MetalCommandBuffer *commandBuffer; 2031 2032 renderer->availableCommandBufferCapacity += allocateCount; 2033 2034 renderer->availableCommandBuffers = SDL_realloc( 2035 renderer->availableCommandBuffers, 2036 sizeof(MetalCommandBuffer *) * renderer->availableCommandBufferCapacity); 2037 2038 for (Uint32 i = 0; i < allocateCount; i += 1) { 2039 commandBuffer = SDL_calloc(1, sizeof(MetalCommandBuffer)); 2040 commandBuffer->renderer = renderer; 2041 2042 // The native Metal command buffer is created in METAL_AcquireCommandBuffer 2043 2044 commandBuffer->windowDataCapacity = 1; 2045 commandBuffer->windowDataCount = 0; 2046 commandBuffer->windowDatas = SDL_calloc( 2047 commandBuffer->windowDataCapacity, sizeof(MetalWindowData *)); 2048 2049 // Reference Counting 2050 commandBuffer->usedBufferCapacity = 4; 2051 commandBuffer->usedBufferCount = 0; 2052 commandBuffer->usedBuffers = SDL_calloc( 2053 commandBuffer->usedBufferCapacity, sizeof(MetalBuffer *)); 2054 2055 commandBuffer->usedTextureCapacity = 4; 2056 commandBuffer->usedTextureCount = 0; 2057 commandBuffer->usedTextures = SDL_calloc( 2058 commandBuffer->usedTextureCapacity, sizeof(MetalTexture *)); 2059 2060 renderer->availableCommandBuffers[renderer->availableCommandBufferCount] = commandBuffer; 2061 renderer->availableCommandBufferCount += 1; 2062 } 2063} 2064 2065static MetalCommandBuffer *METAL_INTERNAL_GetInactiveCommandBufferFromPool( 2066 MetalRenderer *renderer) 2067{ 2068 MetalCommandBuffer *commandBuffer; 2069 2070 if (renderer->availableCommandBufferCount == 0) { 2071 METAL_INTERNAL_AllocateCommandBuffers( 2072 renderer, 2073 renderer->availableCommandBufferCapacity); 2074 } 2075 2076 commandBuffer = renderer->availableCommandBuffers[renderer->availableCommandBufferCount - 1]; 2077 renderer->availableCommandBufferCount -= 1; 2078 2079 return commandBuffer; 2080} 2081 2082static Uint8 METAL_INTERNAL_CreateFence( 2083 MetalRenderer *renderer) 2084{ 2085 MetalFence *fence; 2086 2087 fence = SDL_calloc(1, sizeof(MetalFence)); 2088 SDL_SetAtomicInt(&fence->complete, 0); 2089 SDL_SetAtomicInt(&fence->referenceCount, 0); 2090 2091 // Add it to the available pool 2092 // FIXME: Should this be EXPAND_IF_NEEDED? 2093 if (renderer->availableFenceCount >= renderer->availableFenceCapacity) { 2094 renderer->availableFenceCapacity *= 2; 2095 2096 renderer->availableFences = SDL_realloc( 2097 renderer->availableFences, 2098 sizeof(MetalFence *) * renderer->availableFenceCapacity); 2099 } 2100 2101 renderer->availableFences[renderer->availableFenceCount] = fence; 2102 renderer->availableFenceCount += 1; 2103 2104 return 1; 2105} 2106 2107static bool METAL_INTERNAL_AcquireFence( 2108 MetalRenderer *renderer, 2109 MetalCommandBuffer *commandBuffer) 2110{ 2111 MetalFence *fence; 2112 2113 // Acquire a fence from the pool 2114 SDL_LockMutex(renderer->fenceLock); 2115 2116 if (renderer->availableFenceCount == 0) { 2117 if (!METAL_INTERNAL_CreateFence(renderer)) { 2118 SDL_UnlockMutex(renderer->fenceLock); 2119 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create fence!"); 2120 return false; 2121 } 2122 } 2123 2124 fence = renderer->availableFences[renderer->availableFenceCount - 1]; 2125 renderer->availableFenceCount -= 1; 2126 2127 SDL_UnlockMutex(renderer->fenceLock); 2128 2129 // Associate the fence with the command buffer 2130 commandBuffer->fence = fence; 2131 SDL_SetAtomicInt(&fence->complete, 0); // FIXME: Is this right? 2132 (void)SDL_AtomicIncRef(&commandBuffer->fence->referenceCount); 2133 2134 return true; 2135} 2136 2137static SDL_GPUCommandBuffer *METAL_AcquireCommandBuffer( 2138 SDL_GPURenderer *driverData) 2139{ 2140 @autoreleasepool { 2141 MetalRenderer *renderer = (MetalRenderer *)driverData; 2142 MetalCommandBuffer *commandBuffer; 2143 2144 SDL_LockMutex(renderer->acquireCommandBufferLock); 2145 2146 commandBuffer = METAL_INTERNAL_GetInactiveCommandBufferFromPool(renderer); 2147 commandBuffer->handle = [renderer->queue commandBuffer]; 2148 2149 commandBuffer->graphics_pipeline = NULL; 2150 commandBuffer->compute_pipeline = NULL; 2151 for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) { 2152 commandBuffer->vertexUniformBuffers[i] = NULL; 2153 commandBuffer->fragmentUniformBuffers[i] = NULL; 2154 commandBuffer->computeUniformBuffers[i] = NULL; 2155 } 2156 2157 commandBuffer->autoReleaseFence = true; 2158 2159 SDL_UnlockMutex(renderer->acquireCommandBufferLock); 2160 2161 return (SDL_GPUCommandBuffer *)commandBuffer; 2162 } 2163} 2164 2165// This function assumes that it's called from within an autorelease pool 2166static MetalUniformBuffer *METAL_INTERNAL_AcquireUniformBufferFromPool( 2167 MetalCommandBuffer *commandBuffer) 2168{ 2169 MetalRenderer *renderer = commandBuffer->renderer; 2170 MetalUniformBuffer *uniformBuffer; 2171 2172 SDL_LockMutex(renderer->acquireUniformBufferLock); 2173 2174 if (renderer->uniformBufferPoolCount > 0) { 2175 uniformBuffer = renderer->uniformBufferPool[renderer->uniformBufferPoolCount - 1]; 2176 renderer->uniformBufferPoolCount -= 1; 2177 } else { 2178 uniformBuffer = METAL_INTERNAL_CreateUniformBuffer( 2179 renderer, 2180 UNIFORM_BUFFER_SIZE); 2181 } 2182 2183 SDL_UnlockMutex(renderer->acquireUniformBufferLock); 2184 2185 METAL_INTERNAL_TrackUniformBuffer(commandBuffer, uniformBuffer); 2186 2187 return uniformBuffer; 2188} 2189 2190static void METAL_INTERNAL_ReturnUniformBufferToPool( 2191 MetalRenderer *renderer, 2192 MetalUniformBuffer *uniformBuffer) 2193{ 2194 if (renderer->uniformBufferPoolCount >= renderer->uniformBufferPoolCapacity) { 2195 renderer->uniformBufferPoolCapacity *= 2; 2196 renderer->uniformBufferPool = SDL_realloc( 2197 renderer->uniformBufferPool, 2198 renderer->uniformBufferPoolCapacity * sizeof(MetalUniformBuffer *)); 2199 } 2200 2201 renderer->uniformBufferPool[renderer->uniformBufferPoolCount] = uniformBuffer; 2202 renderer->uniformBufferPoolCount += 1; 2203 2204 uniformBuffer->writeOffset = 0; 2205 uniformBuffer->drawOffset = 0; 2206} 2207 2208static void METAL_SetViewport( 2209 SDL_GPUCommandBuffer *commandBuffer, 2210 const SDL_GPUViewport *viewport) 2211{ 2212 @autoreleasepool { 2213 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2214 MTLViewport metalViewport; 2215 2216 metalViewport.originX = viewport->x; 2217 metalViewport.originY = viewport->y; 2218 metalViewport.width = viewport->w; 2219 metalViewport.height = viewport->h; 2220 metalViewport.znear = viewport->min_depth; 2221 metalViewport.zfar = viewport->max_depth; 2222 2223 [metalCommandBuffer->renderEncoder setViewport:metalViewport]; 2224 } 2225} 2226 2227static void METAL_SetScissor( 2228 SDL_GPUCommandBuffer *commandBuffer, 2229 const SDL_Rect *scissor) 2230{ 2231 @autoreleasepool { 2232 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2233 MTLScissorRect metalScissor; 2234 2235 metalScissor.x = scissor->x; 2236 metalScissor.y = scissor->y; 2237 metalScissor.width = scissor->w; 2238 metalScissor.height = scissor->h; 2239 2240 [metalCommandBuffer->renderEncoder setScissorRect:metalScissor]; 2241 } 2242} 2243 2244static void METAL_SetBlendConstants( 2245 SDL_GPUCommandBuffer *commandBuffer, 2246 SDL_FColor blendConstants) 2247{ 2248 @autoreleasepool { 2249 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2250 [metalCommandBuffer->renderEncoder setBlendColorRed:blendConstants.r 2251 green:blendConstants.g 2252 blue:blendConstants.b 2253 alpha:blendConstants.a]; 2254 } 2255} 2256 2257static void METAL_SetStencilReference( 2258 SDL_GPUCommandBuffer *commandBuffer, 2259 Uint8 reference) 2260{ 2261 @autoreleasepool { 2262 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2263 [metalCommandBuffer->renderEncoder setStencilReferenceValue:reference]; 2264 } 2265} 2266 2267static void METAL_BeginRenderPass( 2268 SDL_GPUCommandBuffer *commandBuffer, 2269 const SDL_GPUColorTargetInfo *colorTargetInfos, 2270 Uint32 numColorTargets, 2271 const SDL_GPUDepthStencilTargetInfo *depthStencilTargetInfo) 2272{ 2273 @autoreleasepool { 2274 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2275 MetalRenderer *renderer = metalCommandBuffer->renderer; 2276 MTLRenderPassDescriptor *passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; 2277 Uint32 vpWidth = UINT_MAX; 2278 Uint32 vpHeight = UINT_MAX; 2279 SDL_GPUViewport viewport; 2280 SDL_Rect scissorRect; 2281 SDL_FColor blendConstants; 2282 2283 for (Uint32 i = 0; i < numColorTargets; i += 1) { 2284 MetalTextureContainer *container = (MetalTextureContainer *)colorTargetInfos[i].texture; 2285 MetalTexture *texture = METAL_INTERNAL_PrepareTextureForWrite( 2286 renderer, 2287 container, 2288 colorTargetInfos[i].cycle); 2289 2290 passDescriptor.colorAttachments[i].texture = texture->handle; 2291 passDescriptor.colorAttachments[i].level = colorTargetInfos[i].mip_level; 2292 if (container->header.info.type == SDL_GPU_TEXTURETYPE_3D) { 2293 passDescriptor.colorAttachments[i].depthPlane = colorTargetInfos[i].layer_or_depth_plane; 2294 } else { 2295 passDescriptor.colorAttachments[i].slice = colorTargetInfos[i].layer_or_depth_plane; 2296 } 2297 passDescriptor.colorAttachments[i].clearColor = MTLClearColorMake( 2298 colorTargetInfos[i].clear_color.r, 2299 colorTargetInfos[i].clear_color.g, 2300 colorTargetInfos[i].clear_color.b, 2301 colorTargetInfos[i].clear_color.a); 2302 passDescriptor.colorAttachments[i].loadAction = SDLToMetal_LoadOp[colorTargetInfos[i].load_op]; 2303 passDescriptor.colorAttachments[i].storeAction = SDLToMetal_StoreOp[colorTargetInfos[i].store_op]; 2304 2305 METAL_INTERNAL_TrackTexture(metalCommandBuffer, texture); 2306 2307 if (colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE || colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) { 2308 MetalTextureContainer *resolveContainer = (MetalTextureContainer *)colorTargetInfos[i].resolve_texture; 2309 MetalTexture *resolveTexture = METAL_INTERNAL_PrepareTextureForWrite( 2310 renderer, 2311 resolveContainer, 2312 colorTargetInfos[i].cycle_resolve_texture); 2313 2314 passDescriptor.colorAttachments[i].resolveTexture = resolveTexture->handle; 2315 passDescriptor.colorAttachments[i].resolveSlice = colorTargetInfos[i].resolve_layer; 2316 passDescriptor.colorAttachments[i].resolveLevel = colorTargetInfos[i].resolve_mip_level; 2317 2318 METAL_INTERNAL_TrackTexture(metalCommandBuffer, resolveTexture); 2319 } 2320 } 2321 2322 if (depthStencilTargetInfo != NULL) { 2323 MetalTextureContainer *container = (MetalTextureContainer *)depthStencilTargetInfo->texture; 2324 MetalTexture *texture = METAL_INTERNAL_PrepareTextureForWrite( 2325 renderer, 2326 container, 2327 depthStencilTargetInfo->cycle); 2328 2329 passDescriptor.depthAttachment.texture = texture->handle; 2330 passDescriptor.depthAttachment.level = depthStencilTargetInfo->mip_level; 2331 passDescriptor.depthAttachment.slice = depthStencilTargetInfo->layer; 2332 passDescriptor.depthAttachment.loadAction = SDLToMetal_LoadOp[depthStencilTargetInfo->load_op]; 2333 passDescriptor.depthAttachment.storeAction = SDLToMetal_StoreOp[depthStencilTargetInfo->store_op]; 2334 passDescriptor.depthAttachment.clearDepth = depthStencilTargetInfo->clear_depth; 2335 2336 if (IsStencilFormat(container->header.info.format)) { 2337 passDescriptor.stencilAttachment.texture = texture->handle; 2338 passDescriptor.stencilAttachment.loadAction = SDLToMetal_LoadOp[depthStencilTargetInfo->stencil_load_op]; 2339 passDescriptor.stencilAttachment.storeAction = SDLToMetal_StoreOp[depthStencilTargetInfo->stencil_store_op]; 2340 passDescriptor.stencilAttachment.clearStencil = depthStencilTargetInfo->clear_stencil; 2341 } 2342 2343 METAL_INTERNAL_TrackTexture(metalCommandBuffer, texture); 2344 } 2345 2346 metalCommandBuffer->renderEncoder = [metalCommandBuffer->handle renderCommandEncoderWithDescriptor:passDescriptor]; 2347 2348 // The viewport cannot be larger than the smallest target. 2349 for (Uint32 i = 0; i < numColorTargets; i += 1) { 2350 MetalTextureContainer *container = (MetalTextureContainer *)colorTargetInfos[i].texture; 2351 Uint32 w = container->header.info.width >> colorTargetInfos[i].mip_level; 2352 Uint32 h = container->header.info.height >> colorTargetInfos[i].mip_level; 2353 2354 if (w < vpWidth) { 2355 vpWidth = w; 2356 } 2357 2358 if (h < vpHeight) { 2359 vpHeight = h; 2360 } 2361 } 2362 2363 if (depthStencilTargetInfo != NULL) { 2364 MetalTextureContainer *container = (MetalTextureContainer *)depthStencilTargetInfo->texture; 2365 Uint32 w = container->header.info.width >> depthStencilTargetInfo->mip_level; 2366 Uint32 h = container->header.info.height >> depthStencilTargetInfo->mip_level; 2367 2368 if (w < vpWidth) { 2369 vpWidth = w; 2370 } 2371 2372 if (h < vpHeight) { 2373 vpHeight = h; 2374 } 2375 } 2376 2377 // Set sensible default states 2378 viewport.x = 0; 2379 viewport.y = 0; 2380 viewport.w = vpWidth; 2381 viewport.h = vpHeight; 2382 viewport.min_depth = 0; 2383 viewport.max_depth = 1; 2384 METAL_SetViewport(commandBuffer, &viewport); 2385 2386 scissorRect.x = 0; 2387 scissorRect.y = 0; 2388 scissorRect.w = vpWidth; 2389 scissorRect.h = vpHeight; 2390 METAL_SetScissor(commandBuffer, &scissorRect); 2391 2392 blendConstants.r = 1.0f; 2393 blendConstants.g = 1.0f; 2394 blendConstants.b = 1.0f; 2395 blendConstants.a = 1.0f; 2396 METAL_SetBlendConstants( 2397 commandBuffer, 2398 blendConstants); 2399 2400 METAL_SetStencilReference( 2401 commandBuffer, 2402 0); 2403 } 2404} 2405 2406static void METAL_BindGraphicsPipeline( 2407 SDL_GPUCommandBuffer *commandBuffer, 2408 SDL_GPUGraphicsPipeline *graphicsPipeline) 2409{ 2410 @autoreleasepool { 2411 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2412 MetalGraphicsPipeline *previousPipeline = metalCommandBuffer->graphics_pipeline; 2413 MetalGraphicsPipeline *pipeline = (MetalGraphicsPipeline *)graphicsPipeline; 2414 SDL_GPURasterizerState *rast = &pipeline->rasterizerState; 2415 Uint32 i; 2416 2417 metalCommandBuffer->graphics_pipeline = pipeline; 2418 2419 [metalCommandBuffer->renderEncoder setRenderPipelineState:pipeline->handle]; 2420 2421 // Apply rasterizer state 2422 [metalCommandBuffer->renderEncoder setTriangleFillMode:SDLToMetal_PolygonMode[pipeline->rasterizerState.fill_mode]]; 2423 [metalCommandBuffer->renderEncoder setCullMode:SDLToMetal_CullMode[pipeline->rasterizerState.cull_mode]]; 2424 [metalCommandBuffer->renderEncoder setFrontFacingWinding:SDLToMetal_FrontFace[pipeline->rasterizerState.front_face]]; 2425#ifndef SDL_PLATFORM_VISIONOS 2426 [metalCommandBuffer->renderEncoder setDepthClipMode:SDLToMetal_DepthClipMode(pipeline->rasterizerState.enable_depth_clip)]; 2427#endif 2428 [metalCommandBuffer->renderEncoder 2429 setDepthBias:((rast->enable_depth_bias) ? rast->depth_bias_constant_factor : 0) 2430 slopeScale:((rast->enable_depth_bias) ? rast->depth_bias_slope_factor : 0) 2431 clamp:((rast->enable_depth_bias) ? rast->depth_bias_clamp : 0)]; 2432 2433 // Apply depth-stencil state 2434 if (pipeline->depth_stencil_state != NULL) { 2435 [metalCommandBuffer->renderEncoder 2436 setDepthStencilState:pipeline->depth_stencil_state]; 2437 } 2438 2439 for (i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) { 2440 metalCommandBuffer->needVertexUniformBufferBind[i] = true; 2441 metalCommandBuffer->needFragmentUniformBufferBind[i] = true; 2442 } 2443 2444 for (i = 0; i < pipeline->header.num_vertex_uniform_buffers; i += 1) { 2445 if (metalCommandBuffer->vertexUniformBuffers[i] == NULL) { 2446 metalCommandBuffer->vertexUniformBuffers[i] = METAL_INTERNAL_AcquireUniformBufferFromPool( 2447 metalCommandBuffer); 2448 } 2449 } 2450 2451 for (i = 0; i < pipeline->header.num_fragment_uniform_buffers; i += 1) { 2452 if (metalCommandBuffer->fragmentUniformBuffers[i] == NULL) { 2453 metalCommandBuffer->fragmentUniformBuffers[i] = METAL_INTERNAL_AcquireUniformBufferFromPool( 2454 metalCommandBuffer); 2455 } 2456 } 2457 2458 if (previousPipeline && previousPipeline != pipeline) { 2459 // if the number of uniform buffers has changed, the storage buffers will move as well 2460 // and need a rebind at their new locations 2461 if (previousPipeline->header.num_vertex_uniform_buffers != pipeline->header.num_vertex_uniform_buffers) { 2462 metalCommandBuffer->needVertexStorageBufferBind = true; 2463 } 2464 if (previousPipeline->header.num_fragment_uniform_buffers != pipeline->header.num_fragment_uniform_buffers) { 2465 metalCommandBuffer->needFragmentStorageBufferBind = true; 2466 } 2467 } 2468 } 2469} 2470 2471static void METAL_BindVertexBuffers( 2472 SDL_GPUCommandBuffer *commandBuffer, 2473 Uint32 firstSlot, 2474 const SDL_GPUBufferBinding *bindings, 2475 Uint32 numBindings) 2476{ 2477 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2478 2479 for (Uint32 i = 0; i < numBindings; i += 1) { 2480 MetalBuffer *currentBuffer = ((MetalBufferContainer *)bindings[i].buffer)->activeBuffer; 2481 if (metalCommandBuffer->vertexBuffers[firstSlot + i] != currentBuffer->handle || metalCommandBuffer->vertexBufferOffsets[firstSlot + i] != bindings[i].offset) { 2482 metalCommandBuffer->vertexBuffers[firstSlot + i] = currentBuffer->handle; 2483 metalCommandBuffer->vertexBufferOffsets[firstSlot + i] = bindings[i].offset; 2484 metalCommandBuffer->needVertexBufferBind = true; 2485 METAL_INTERNAL_TrackBuffer(metalCommandBuffer, currentBuffer); 2486 } 2487 } 2488 2489 metalCommandBuffer->vertexBufferCount = 2490 SDL_max(metalCommandBuffer->vertexBufferCount, firstSlot + numBindings); 2491} 2492 2493static void METAL_BindIndexBuffer( 2494 SDL_GPUCommandBuffer *commandBuffer, 2495 const SDL_GPUBufferBinding *binding, 2496 SDL_GPUIndexElementSize indexElementSize) 2497{ 2498 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2499 metalCommandBuffer->indexBuffer = ((MetalBufferContainer *)binding->buffer)->activeBuffer; 2500 metalCommandBuffer->indexBufferOffset = binding->offset; 2501 metalCommandBuffer->index_element_size = indexElementSize; 2502 2503 METAL_INTERNAL_TrackBuffer(metalCommandBuffer, metalCommandBuffer->indexBuffer); 2504} 2505 2506static void METAL_BindVertexSamplers( 2507 SDL_GPUCommandBuffer *commandBuffer, 2508 Uint32 firstSlot, 2509 const SDL_GPUTextureSamplerBinding *textureSamplerBindings, 2510 Uint32 numBindings) 2511{ 2512 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2513 MetalTextureContainer *textureContainer; 2514 MetalSampler *sampler; 2515 2516 for (Uint32 i = 0; i < numBindings; i += 1) { 2517 textureContainer = (MetalTextureContainer *)textureSamplerBindings[i].texture; 2518 sampler = (MetalSampler *)textureSamplerBindings[i].sampler; 2519 2520 if (metalCommandBuffer->vertexSamplers[firstSlot + i] != sampler->handle) { 2521 metalCommandBuffer->vertexSamplers[firstSlot + i] = sampler->handle; 2522 metalCommandBuffer->needVertexSamplerBind = true; 2523 } 2524 2525 if (metalCommandBuffer->vertexTextures[firstSlot + i] != textureContainer->activeTexture->handle) { 2526 METAL_INTERNAL_TrackTexture( 2527 metalCommandBuffer, 2528 textureContainer->activeTexture); 2529 2530 metalCommandBuffer->vertexTextures[firstSlot + i] = 2531 textureContainer->activeTexture->handle; 2532 2533 metalCommandBuffer->needVertexSamplerBind = true; 2534 } 2535 } 2536} 2537 2538static void METAL_BindVertexStorageTextures( 2539 SDL_GPUCommandBuffer *commandBuffer, 2540 Uint32 firstSlot, 2541 SDL_GPUTexture *const *storageTextures, 2542 Uint32 numBindings) 2543{ 2544 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2545 MetalTextureContainer *textureContainer; 2546 2547 for (Uint32 i = 0; i < numBindings; i += 1) { 2548 textureContainer = (MetalTextureContainer *)storageTextures[i]; 2549 2550 if (metalCommandBuffer->vertexStorageTextures[firstSlot + i] != textureContainer->activeTexture->handle) { 2551 METAL_INTERNAL_TrackTexture( 2552 metalCommandBuffer, 2553 textureContainer->activeTexture); 2554 2555 metalCommandBuffer->vertexStorageTextures[firstSlot + i] = 2556 textureContainer->activeTexture->handle; 2557 2558 metalCommandBuffer->needVertexStorageTextureBind = true; 2559 } 2560 } 2561} 2562 2563static void METAL_BindVertexStorageBuffers( 2564 SDL_GPUCommandBuffer *commandBuffer, 2565 Uint32 firstSlot, 2566 SDL_GPUBuffer *const *storageBuffers, 2567 Uint32 numBindings) 2568{ 2569 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2570 MetalBufferContainer *bufferContainer; 2571 2572 for (Uint32 i = 0; i < numBindings; i += 1) { 2573 bufferContainer = (MetalBufferContainer *)storageBuffers[i]; 2574 2575 if (metalCommandBuffer->vertexStorageBuffers[firstSlot + i] != bufferContainer->activeBuffer->handle) { 2576 METAL_INTERNAL_TrackBuffer( 2577 metalCommandBuffer, 2578 bufferContainer->activeBuffer); 2579 2580 metalCommandBuffer->vertexStorageBuffers[firstSlot + i] = 2581 bufferContainer->activeBuffer->handle; 2582 2583 metalCommandBuffer->needVertexStorageBufferBind = true; 2584 } 2585 } 2586} 2587 2588static void METAL_BindFragmentSamplers( 2589 SDL_GPUCommandBuffer *commandBuffer, 2590 Uint32 firstSlot, 2591 const SDL_GPUTextureSamplerBinding *textureSamplerBindings, 2592 Uint32 numBindings) 2593{ 2594 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2595 MetalTextureContainer *textureContainer; 2596 MetalSampler *sampler; 2597 2598 for (Uint32 i = 0; i < numBindings; i += 1) { 2599 textureContainer = (MetalTextureContainer *)textureSamplerBindings[i].texture; 2600 sampler = (MetalSampler *)textureSamplerBindings[i].sampler; 2601 2602 if (metalCommandBuffer->fragmentSamplers[firstSlot + i] != sampler->handle) { 2603 metalCommandBuffer->fragmentSamplers[firstSlot + i] = sampler->handle; 2604 metalCommandBuffer->needFragmentSamplerBind = true; 2605 } 2606 2607 if (metalCommandBuffer->fragmentTextures[firstSlot + i] != textureContainer->activeTexture->handle) { 2608 METAL_INTERNAL_TrackTexture( 2609 metalCommandBuffer, 2610 textureContainer->activeTexture); 2611 2612 metalCommandBuffer->fragmentTextures[firstSlot + i] = 2613 textureContainer->activeTexture->handle; 2614 2615 metalCommandBuffer->needFragmentSamplerBind = true; 2616 } 2617 } 2618} 2619 2620static void METAL_BindFragmentStorageTextures( 2621 SDL_GPUCommandBuffer *commandBuffer, 2622 Uint32 firstSlot, 2623 SDL_GPUTexture *const *storageTextures, 2624 Uint32 numBindings) 2625{ 2626 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2627 MetalTextureContainer *textureContainer; 2628 2629 for (Uint32 i = 0; i < numBindings; i += 1) { 2630 textureContainer = (MetalTextureContainer *)storageTextures[i]; 2631 2632 if (metalCommandBuffer->fragmentStorageTextures[firstSlot + i] != textureContainer->activeTexture->handle) { 2633 METAL_INTERNAL_TrackTexture( 2634 metalCommandBuffer, 2635 textureContainer->activeTexture); 2636 2637 metalCommandBuffer->fragmentStorageTextures[firstSlot + i] = 2638 textureContainer->activeTexture->handle; 2639 2640 metalCommandBuffer->needFragmentStorageTextureBind = true; 2641 } 2642 } 2643} 2644 2645static void METAL_BindFragmentStorageBuffers( 2646 SDL_GPUCommandBuffer *commandBuffer, 2647 Uint32 firstSlot, 2648 SDL_GPUBuffer *const *storageBuffers, 2649 Uint32 numBindings) 2650{ 2651 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2652 MetalBufferContainer *bufferContainer; 2653 2654 for (Uint32 i = 0; i < numBindings; i += 1) { 2655 bufferContainer = (MetalBufferContainer *)storageBuffers[i]; 2656 2657 if (metalCommandBuffer->fragmentStorageBuffers[firstSlot + i] != bufferContainer->activeBuffer->handle) { 2658 METAL_INTERNAL_TrackBuffer( 2659 metalCommandBuffer, 2660 bufferContainer->activeBuffer); 2661 2662 metalCommandBuffer->fragmentStorageBuffers[firstSlot + i] = 2663 bufferContainer->activeBuffer->handle; 2664 2665 metalCommandBuffer->needFragmentStorageBufferBind = true; 2666 } 2667 } 2668} 2669 2670// This function assumes that it's called from within an autorelease pool 2671static void METAL_INTERNAL_BindGraphicsResources( 2672 MetalCommandBuffer *commandBuffer) 2673{ 2674 MetalGraphicsPipeline *graphicsPipeline = commandBuffer->graphics_pipeline; 2675 NSUInteger offsets[MAX_STORAGE_BUFFERS_PER_STAGE] = { 0 }; 2676 2677 // Vertex Buffers 2678 if (commandBuffer->needVertexBufferBind) { 2679 id<MTLBuffer> metalBuffers[MAX_VERTEX_BUFFERS]; 2680 NSUInteger bufferOffsets[MAX_VERTEX_BUFFERS]; 2681 NSRange range = NSMakeRange(METAL_FIRST_VERTEX_BUFFER_SLOT, commandBuffer->vertexBufferCount); 2682 for (Uint32 i = 0; i < commandBuffer->vertexBufferCount; i += 1) { 2683 metalBuffers[i] = commandBuffer->vertexBuffers[i]; 2684 bufferOffsets[i] = commandBuffer->vertexBufferOffsets[i]; 2685 } 2686 [commandBuffer->renderEncoder setVertexBuffers:metalBuffers offsets:bufferOffsets withRange:range]; 2687 commandBuffer->needVertexBufferBind = false; 2688 } 2689 2690 // Vertex Samplers+Textures 2691 2692 if (commandBuffer->needVertexSamplerBind) { 2693 if (graphicsPipeline->header.num_vertex_samplers > 0) { 2694 [commandBuffer->renderEncoder setVertexSamplerStates:commandBuffer->vertexSamplers 2695 withRange:NSMakeRange(0, graphicsPipeline->header.num_vertex_samplers)]; 2696 [commandBuffer->renderEncoder setVertexTextures:commandBuffer->vertexTextures 2697 withRange:NSMakeRange(0, graphicsPipeline->header.num_vertex_samplers)]; 2698 } 2699 commandBuffer->needVertexSamplerBind = false; 2700 } 2701 2702 // Vertex Storage Textures 2703 2704 if (commandBuffer->needVertexStorageTextureBind) { 2705 if (graphicsPipeline->header.num_vertex_storage_textures > 0) { 2706 [commandBuffer->renderEncoder setVertexTextures:commandBuffer->vertexStorageTextures 2707 withRange:NSMakeRange(graphicsPipeline->header.num_vertex_samplers, 2708 graphicsPipeline->header.num_vertex_storage_textures)]; 2709 } 2710 commandBuffer->needVertexStorageTextureBind = false; 2711 } 2712 2713 // Vertex Storage Buffers 2714 2715 if (commandBuffer->needVertexStorageBufferBind) { 2716 if (graphicsPipeline->header.num_vertex_storage_buffers > 0) { 2717 [commandBuffer->renderEncoder setVertexBuffers:commandBuffer->vertexStorageBuffers 2718 offsets:offsets 2719 withRange:NSMakeRange(graphicsPipeline->header.num_vertex_uniform_buffers, 2720 graphicsPipeline->header.num_vertex_storage_buffers)]; 2721 } 2722 commandBuffer->needVertexStorageBufferBind = false; 2723 } 2724 2725 // Vertex Uniform Buffers 2726 2727 for (Uint32 i = 0; i < graphicsPipeline->header.num_vertex_uniform_buffers; i += 1) { 2728 if (commandBuffer->needVertexUniformBufferBind[i]) { 2729 if (graphicsPipeline->header.num_vertex_uniform_buffers > i) { 2730 [commandBuffer->renderEncoder 2731 setVertexBuffer:commandBuffer->vertexUniformBuffers[i]->handle 2732 offset:commandBuffer->vertexUniformBuffers[i]->drawOffset 2733 atIndex:i]; 2734 } 2735 commandBuffer->needVertexUniformBufferBind[i] = false; 2736 } 2737 } 2738 2739 // Fragment Samplers+Textures 2740 2741 if (commandBuffer->needFragmentSamplerBind) { 2742 if (graphicsPipeline->header.num_fragment_samplers > 0) { 2743 [commandBuffer->renderEncoder setFragmentSamplerStates:commandBuffer->fragmentSamplers 2744 withRange:NSMakeRange(0, graphicsPipeline->header.num_fragment_samplers)]; 2745 [commandBuffer->renderEncoder setFragmentTextures:commandBuffer->fragmentTextures 2746 withRange:NSMakeRange(0, graphicsPipeline->header.num_fragment_samplers)]; 2747 } 2748 commandBuffer->needFragmentSamplerBind = false; 2749 } 2750 2751 // Fragment Storage Textures 2752 2753 if (commandBuffer->needFragmentStorageTextureBind) { 2754 if (graphicsPipeline->header.num_fragment_storage_textures > 0) { 2755 [commandBuffer->renderEncoder setFragmentTextures:commandBuffer->fragmentStorageTextures 2756 withRange:NSMakeRange(graphicsPipeline->header.num_fragment_samplers, 2757 graphicsPipeline->header.num_fragment_storage_textures)]; 2758 } 2759 commandBuffer->needFragmentStorageTextureBind = false; 2760 } 2761 2762 // Fragment Storage Buffers 2763 2764 if (commandBuffer->needFragmentStorageBufferBind) { 2765 if (graphicsPipeline->header.num_fragment_storage_buffers > 0) { 2766 [commandBuffer->renderEncoder setFragmentBuffers:commandBuffer->fragmentStorageBuffers 2767 offsets:offsets 2768 withRange:NSMakeRange(graphicsPipeline->header.num_fragment_uniform_buffers, 2769 graphicsPipeline->header.num_fragment_storage_buffers)]; 2770 } 2771 commandBuffer->needFragmentStorageBufferBind = false; 2772 } 2773 2774 // Fragment Uniform Buffers 2775 2776 for (Uint32 i = 0; i < graphicsPipeline->header.num_fragment_uniform_buffers; i += 1) { 2777 if (commandBuffer->needFragmentUniformBufferBind[i]) { 2778 if (graphicsPipeline->header.num_fragment_uniform_buffers > i) { 2779 [commandBuffer->renderEncoder 2780 setFragmentBuffer:commandBuffer->fragmentUniformBuffers[i]->handle 2781 offset:commandBuffer->fragmentUniformBuffers[i]->drawOffset 2782 atIndex:i]; 2783 } 2784 commandBuffer->needFragmentUniformBufferBind[i] = false; 2785 } 2786 } 2787} 2788 2789// This function assumes that it's called from within an autorelease pool 2790static void METAL_INTERNAL_BindComputeResources( 2791 MetalCommandBuffer *commandBuffer) 2792{ 2793 MetalComputePipeline *computePipeline = commandBuffer->compute_pipeline; 2794 NSUInteger offsets[MAX_STORAGE_BUFFERS_PER_STAGE] = { 0 }; 2795 2796 if (commandBuffer->needComputeSamplerBind) { 2797 if (computePipeline->header.numSamplers > 0) { 2798 [commandBuffer->computeEncoder setTextures:commandBuffer->computeSamplerTextures 2799 withRange:NSMakeRange(0, computePipeline->header.numSamplers)]; 2800 [commandBuffer->computeEncoder setSamplerStates:commandBuffer->computeSamplers 2801 withRange:NSMakeRange(0, computePipeline->header.numSamplers)]; 2802 } 2803 commandBuffer->needComputeSamplerBind = false; 2804 } 2805 2806 if (commandBuffer->needComputeReadOnlyStorageTextureBind) { 2807 if (computePipeline->header.numReadonlyStorageTextures > 0) { 2808 [commandBuffer->computeEncoder setTextures:commandBuffer->computeReadOnlyTextures 2809 withRange:NSMakeRange( 2810 computePipeline->header.numSamplers, 2811 computePipeline->header.numReadonlyStorageTextures)]; 2812 } 2813 commandBuffer->needComputeReadOnlyStorageTextureBind = false; 2814 } 2815 2816 if (commandBuffer->needComputeReadOnlyStorageBufferBind) { 2817 if (computePipeline->header.numReadonlyStorageBuffers > 0) { 2818 [commandBuffer->computeEncoder setBuffers:commandBuffer->computeReadOnlyBuffers 2819 offsets:offsets 2820 withRange:NSMakeRange(computePipeline->header.numUniformBuffers, 2821 computePipeline->header.numReadonlyStorageBuffers)]; 2822 } 2823 commandBuffer->needComputeReadOnlyStorageBufferBind = false; 2824 } 2825 2826 for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) { 2827 if (commandBuffer->needComputeUniformBufferBind[i]) { 2828 if (computePipeline->header.numUniformBuffers > i) { 2829 [commandBuffer->computeEncoder 2830 setBuffer:commandBuffer->computeUniformBuffers[i]->handle 2831 offset:commandBuffer->computeUniformBuffers[i]->drawOffset 2832 atIndex:i]; 2833 } 2834 } 2835 commandBuffer->needComputeUniformBufferBind[i] = false; 2836 } 2837} 2838 2839static void METAL_DrawIndexedPrimitives( 2840 SDL_GPUCommandBuffer *commandBuffer, 2841 Uint32 numIndices, 2842 Uint32 numInstances, 2843 Uint32 firstIndex, 2844 Sint32 vertexOffset, 2845 Uint32 firstInstance) 2846{ 2847 @autoreleasepool { 2848 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2849 SDL_GPUPrimitiveType primitiveType = metalCommandBuffer->graphics_pipeline->primitiveType; 2850 Uint32 indexSize = IndexSize(metalCommandBuffer->index_element_size); 2851 2852 METAL_INTERNAL_BindGraphicsResources(metalCommandBuffer); 2853 2854 [metalCommandBuffer->renderEncoder 2855 drawIndexedPrimitives:SDLToMetal_PrimitiveType[primitiveType] 2856 indexCount:numIndices 2857 indexType:SDLToMetal_IndexType[metalCommandBuffer->index_element_size] 2858 indexBuffer:metalCommandBuffer->indexBuffer->handle 2859 indexBufferOffset:metalCommandBuffer->indexBufferOffset + (firstIndex * indexSize) 2860 instanceCount:numInstances 2861 baseVertex:vertexOffset 2862 baseInstance:firstInstance]; 2863 } 2864} 2865 2866static void METAL_DrawPrimitives( 2867 SDL_GPUCommandBuffer *commandBuffer, 2868 Uint32 numVertices, 2869 Uint32 numInstances, 2870 Uint32 firstVertex, 2871 Uint32 firstInstance) 2872{ 2873 @autoreleasepool { 2874 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2875 SDL_GPUPrimitiveType primitiveType = metalCommandBuffer->graphics_pipeline->primitiveType; 2876 2877 METAL_INTERNAL_BindGraphicsResources(metalCommandBuffer); 2878 2879 [metalCommandBuffer->renderEncoder 2880 drawPrimitives:SDLToMetal_PrimitiveType[primitiveType] 2881 vertexStart:firstVertex 2882 vertexCount:numVertices 2883 instanceCount:numInstances 2884 baseInstance:firstInstance]; 2885 } 2886} 2887 2888static void METAL_DrawPrimitivesIndirect( 2889 SDL_GPUCommandBuffer *commandBuffer, 2890 SDL_GPUBuffer *buffer, 2891 Uint32 offset, 2892 Uint32 drawCount) 2893{ 2894 @autoreleasepool { 2895 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2896 MetalBuffer *metalBuffer = ((MetalBufferContainer *)buffer)->activeBuffer; 2897 SDL_GPUPrimitiveType primitiveType = metalCommandBuffer->graphics_pipeline->primitiveType; 2898 2899 METAL_INTERNAL_BindGraphicsResources(metalCommandBuffer); 2900 2901 /* Metal: "We have multi-draw at home!" 2902 * Multi-draw at home: 2903 */ 2904 for (Uint32 i = 0; i < drawCount; i += 1) { 2905 [metalCommandBuffer->renderEncoder 2906 drawPrimitives:SDLToMetal_PrimitiveType[primitiveType] 2907 indirectBuffer:metalBuffer->handle 2908 indirectBufferOffset:offset + (sizeof(SDL_GPUIndirectDrawCommand) * i)]; 2909 } 2910 2911 METAL_INTERNAL_TrackBuffer(metalCommandBuffer, metalBuffer); 2912 } 2913} 2914 2915static void METAL_DrawIndexedPrimitivesIndirect( 2916 SDL_GPUCommandBuffer *commandBuffer, 2917 SDL_GPUBuffer *buffer, 2918 Uint32 offset, 2919 Uint32 drawCount) 2920{ 2921 @autoreleasepool { 2922 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2923 MetalBuffer *metalBuffer = ((MetalBufferContainer *)buffer)->activeBuffer; 2924 SDL_GPUPrimitiveType primitiveType = metalCommandBuffer->graphics_pipeline->primitiveType; 2925 2926 METAL_INTERNAL_BindGraphicsResources(metalCommandBuffer); 2927 2928 for (Uint32 i = 0; i < drawCount; i += 1) { 2929 [metalCommandBuffer->renderEncoder 2930 drawIndexedPrimitives:SDLToMetal_PrimitiveType[primitiveType] 2931 indexType:SDLToMetal_IndexType[metalCommandBuffer->index_element_size] 2932 indexBuffer:metalCommandBuffer->indexBuffer->handle 2933 indexBufferOffset:metalCommandBuffer->indexBufferOffset 2934 indirectBuffer:metalBuffer->handle 2935 indirectBufferOffset:offset + (sizeof(SDL_GPUIndexedIndirectDrawCommand) * i)]; 2936 } 2937 2938 METAL_INTERNAL_TrackBuffer(metalCommandBuffer, metalBuffer); 2939 } 2940} 2941 2942static void METAL_EndRenderPass( 2943 SDL_GPUCommandBuffer *commandBuffer) 2944{ 2945 @autoreleasepool { 2946 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 2947 [metalCommandBuffer->renderEncoder endEncoding]; 2948 metalCommandBuffer->renderEncoder = nil; 2949 2950 for (Uint32 i = 0; i < MAX_VERTEX_BUFFERS; i += 1) { 2951 metalCommandBuffer->vertexBuffers[i] = nil; 2952 metalCommandBuffer->vertexBufferOffsets[i] = 0; 2953 metalCommandBuffer->vertexBufferCount = 0; 2954 } 2955 for (Uint32 i = 0; i < MAX_TEXTURE_SAMPLERS_PER_STAGE; i += 1) { 2956 metalCommandBuffer->vertexSamplers[i] = nil; 2957 metalCommandBuffer->vertexTextures[i] = nil; 2958 metalCommandBuffer->fragmentSamplers[i] = nil; 2959 metalCommandBuffer->fragmentTextures[i] = nil; 2960 } 2961 for (Uint32 i = 0; i < MAX_STORAGE_TEXTURES_PER_STAGE; i += 1) { 2962 metalCommandBuffer->vertexStorageTextures[i] = nil; 2963 metalCommandBuffer->fragmentStorageTextures[i] = nil; 2964 } 2965 for (Uint32 i = 0; i < MAX_STORAGE_BUFFERS_PER_STAGE; i += 1) { 2966 metalCommandBuffer->vertexStorageBuffers[i] = nil; 2967 metalCommandBuffer->fragmentStorageBuffers[i] = nil; 2968 } 2969 } 2970} 2971 2972// This function assumes that it's called from within an autorelease pool 2973static void METAL_INTERNAL_PushUniformData( 2974 MetalCommandBuffer *metalCommandBuffer, 2975 SDL_GPUShaderStage shaderStage, 2976 Uint32 slotIndex, 2977 const void *data, 2978 Uint32 length) 2979{ 2980 MetalUniformBuffer *metalUniformBuffer; 2981 Uint32 alignedDataLength; 2982 2983 if (shaderStage == SDL_GPU_SHADERSTAGE_VERTEX) { 2984 if (metalCommandBuffer->vertexUniformBuffers[slotIndex] == NULL) { 2985 metalCommandBuffer->vertexUniformBuffers[slotIndex] = METAL_INTERNAL_AcquireUniformBufferFromPool( 2986 metalCommandBuffer); 2987 } 2988 metalUniformBuffer = metalCommandBuffer->vertexUniformBuffers[slotIndex]; 2989 } else if (shaderStage == SDL_GPU_SHADERSTAGE_FRAGMENT) { 2990 if (metalCommandBuffer->fragmentUniformBuffers[slotIndex] == NULL) { 2991 metalCommandBuffer->fragmentUniformBuffers[slotIndex] = METAL_INTERNAL_AcquireUniformBufferFromPool( 2992 metalCommandBuffer); 2993 } 2994 metalUniformBuffer = metalCommandBuffer->fragmentUniformBuffers[slotIndex]; 2995 } else if (shaderStage == SDL_GPU_SHADERSTAGE_COMPUTE) { 2996 if (metalCommandBuffer->computeUniformBuffers[slotIndex] == NULL) { 2997 metalCommandBuffer->computeUniformBuffers[slotIndex] = METAL_INTERNAL_AcquireUniformBufferFromPool( 2998 metalCommandBuffer); 2999 } 3000 metalUniformBuffer = metalCommandBuffer->computeUniformBuffers[slotIndex]; 3001 } else { 3002 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!"); 3003 return; 3004 } 3005 3006 alignedDataLength = METAL_INTERNAL_NextHighestAlignment( 3007 length, 3008 256); 3009 3010 if (metalUniformBuffer->writeOffset + alignedDataLength >= UNIFORM_BUFFER_SIZE) { 3011 metalUniformBuffer = METAL_INTERNAL_AcquireUniformBufferFromPool( 3012 metalCommandBuffer); 3013 3014 metalUniformBuffer->writeOffset = 0; 3015 metalUniformBuffer->drawOffset = 0; 3016 3017 if (shaderStage == SDL_GPU_SHADERSTAGE_VERTEX) { 3018 metalCommandBuffer->vertexUniformBuffers[slotIndex] = metalUniformBuffer; 3019 } else if (shaderStage == SDL_GPU_SHADERSTAGE_FRAGMENT) { 3020 metalCommandBuffer->fragmentUniformBuffers[slotIndex] = metalUniformBuffer; 3021 } else if (shaderStage == SDL_GPU_SHADERSTAGE_COMPUTE) { 3022 metalCommandBuffer->computeUniformBuffers[slotIndex] = metalUniformBuffer; 3023 } else { 3024 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!"); 3025 return; 3026 } 3027 } 3028 3029 metalUniformBuffer->drawOffset = metalUniformBuffer->writeOffset; 3030 3031 SDL_memcpy( 3032 (metalUniformBuffer->handle).contents + metalUniformBuffer->writeOffset, 3033 data, 3034 length); 3035 3036 metalUniformBuffer->writeOffset += alignedDataLength; 3037 3038 if (shaderStage == SDL_GPU_SHADERSTAGE_VERTEX) { 3039 metalCommandBuffer->needVertexUniformBufferBind[slotIndex] = true; 3040 } else if (shaderStage == SDL_GPU_SHADERSTAGE_FRAGMENT) { 3041 metalCommandBuffer->needFragmentUniformBufferBind[slotIndex] = true; 3042 } else if (shaderStage == SDL_GPU_SHADERSTAGE_COMPUTE) { 3043 metalCommandBuffer->needComputeUniformBufferBind[slotIndex] = true; 3044 } else { 3045 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!"); 3046 } 3047} 3048 3049static void METAL_PushVertexUniformData( 3050 SDL_GPUCommandBuffer *commandBuffer, 3051 Uint32 slotIndex, 3052 const void *data, 3053 Uint32 length) 3054{ 3055 @autoreleasepool { 3056 METAL_INTERNAL_PushUniformData( 3057 (MetalCommandBuffer *)commandBuffer, 3058 SDL_GPU_SHADERSTAGE_VERTEX, 3059 slotIndex, 3060 data, 3061 length); 3062 } 3063} 3064 3065static void METAL_PushFragmentUniformData( 3066 SDL_GPUCommandBuffer *commandBuffer, 3067 Uint32 slotIndex, 3068 const void *data, 3069 Uint32 length) 3070{ 3071 @autoreleasepool { 3072 METAL_INTERNAL_PushUniformData( 3073 (MetalCommandBuffer *)commandBuffer, 3074 SDL_GPU_SHADERSTAGE_FRAGMENT, 3075 slotIndex, 3076 data, 3077 length); 3078 } 3079} 3080 3081// Blit 3082 3083static void METAL_Blit( 3084 SDL_GPUCommandBuffer *commandBuffer, 3085 const SDL_GPUBlitInfo *info) 3086{ 3087 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 3088 MetalRenderer *renderer = (MetalRenderer *)metalCommandBuffer->renderer; 3089 3090 SDL_GPU_BlitCommon( 3091 commandBuffer, 3092 info, 3093 renderer->blitLinearSampler, 3094 renderer->blitNearestSampler, 3095 renderer->blitVertexShader, 3096 renderer->blitFrom2DShader, 3097 renderer->blitFrom2DArrayShader, 3098 renderer->blitFrom3DShader, 3099 renderer->blitFromCubeShader, 3100 renderer->blitFromCubeArrayShader, 3101 &renderer->blitPipelines, 3102 &renderer->blitPipelineCount, 3103 &renderer->blitPipelineCapacity); 3104} 3105 3106// Compute State 3107 3108static void METAL_BeginComputePass( 3109 SDL_GPUCommandBuffer *commandBuffer, 3110 const SDL_GPUStorageTextureReadWriteBinding *storageTextureBindings, 3111 Uint32 numStorageTextureBindings, 3112 const SDL_GPUStorageBufferReadWriteBinding *storageBufferBindings, 3113 Uint32 numStorageBufferBindings) 3114{ 3115 @autoreleasepool { 3116 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 3117 MetalTextureContainer *textureContainer; 3118 MetalTexture *texture; 3119 id<MTLTexture> textureView; 3120 MetalBufferContainer *bufferContainer; 3121 MetalBuffer *buffer; 3122 3123 metalCommandBuffer->computeEncoder = [metalCommandBuffer->handle computeCommandEncoder]; 3124 3125 for (Uint32 i = 0; i < numStorageTextureBindings; i += 1) { 3126 textureContainer = (MetalTextureContainer *)storageTextureBindings[i].texture; 3127 3128 texture = METAL_INTERNAL_PrepareTextureForWrite( 3129 metalCommandBuffer->renderer, 3130 textureContainer, 3131 storageTextureBindings[i].cycle); 3132 3133 METAL_INTERNAL_TrackTexture(metalCommandBuffer, texture); 3134 3135 textureView = [texture->handle newTextureViewWithPixelFormat:SDLToMetal_TextureFormat(textureContainer->header.info.format) 3136 textureType:SDLToMetal_TextureType(textureContainer->header.info.type, false) 3137 levels:NSMakeRange(storageTextureBindings[i].mip_level, 1) 3138 slices:NSMakeRange(storageTextureBindings[i].layer, 1)]; 3139 3140 metalCommandBuffer->computeReadWriteTextures[i] = textureView; 3141 } 3142 3143 for (Uint32 i = 0; i < numStorageBufferBindings; i += 1) { 3144 bufferContainer = (MetalBufferContainer *)storageBufferBindings[i].buffer; 3145 3146 buffer = METAL_INTERNAL_PrepareBufferForWrite( 3147 metalCommandBuffer->renderer, 3148 bufferContainer, 3149 storageBufferBindings[i].cycle); 3150 3151 METAL_INTERNAL_TrackBuffer( 3152 metalCommandBuffer, 3153 buffer); 3154 3155 metalCommandBuffer->computeReadWriteBuffers[i] = buffer->handle; 3156 } 3157 } 3158} 3159 3160static void METAL_BindComputePipeline( 3161 SDL_GPUCommandBuffer *commandBuffer, 3162 SDL_GPUComputePipeline *computePipeline) 3163{ 3164 @autoreleasepool { 3165 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 3166 MetalComputePipeline *pipeline = (MetalComputePipeline *)computePipeline; 3167 3168 metalCommandBuffer->compute_pipeline = pipeline; 3169 3170 [metalCommandBuffer->computeEncoder setComputePipelineState:pipeline->handle]; 3171 3172 for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) { 3173 metalCommandBuffer->needComputeUniformBufferBind[i] = true; 3174 } 3175 3176 for (Uint32 i = 0; i < pipeline->header.numUniformBuffers; i += 1) { 3177 if (metalCommandBuffer->computeUniformBuffers[i] == NULL) { 3178 metalCommandBuffer->computeUniformBuffers[i] = METAL_INTERNAL_AcquireUniformBufferFromPool( 3179 metalCommandBuffer); 3180 } 3181 } 3182 3183 // Bind write-only resources 3184 if (pipeline->header.numReadWriteStorageTextures > 0) { 3185 [metalCommandBuffer->computeEncoder setTextures:metalCommandBuffer->computeReadWriteTextures 3186 withRange:NSMakeRange( 3187 pipeline->header.numSamplers + 3188 pipeline->header.numReadonlyStorageTextures, 3189 pipeline->header.numReadWriteStorageTextures)]; 3190 } 3191 3192 NSUInteger offsets[MAX_COMPUTE_WRITE_BUFFERS] = { 0 }; 3193 if (pipeline->header.numReadWriteStorageBuffers > 0) { 3194 [metalCommandBuffer->computeEncoder setBuffers:metalCommandBuffer->computeReadWriteBuffers 3195 offsets:offsets 3196 withRange:NSMakeRange( 3197 pipeline->header.numUniformBuffers + 3198 pipeline->header.numReadonlyStorageBuffers, 3199 pipeline->header.numReadWriteStorageBuffers)]; 3200 } 3201 } 3202} 3203 3204static void METAL_BindComputeSamplers( 3205 SDL_GPUCommandBuffer *commandBuffer, 3206 Uint32 firstSlot, 3207 const SDL_GPUTextureSamplerBinding *textureSamplerBindings, 3208 Uint32 numBindings) 3209{ 3210 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 3211 MetalTextureContainer *textureContainer; 3212 MetalSampler *sampler; 3213 3214 for (Uint32 i = 0; i < numBindings; i += 1) { 3215 textureContainer = (MetalTextureContainer *)textureSamplerBindings[i].texture; 3216 sampler = (MetalSampler *)textureSamplerBindings[i].sampler; 3217 3218 if (metalCommandBuffer->computeSamplers[firstSlot + i] != sampler->handle) { 3219 metalCommandBuffer->computeSamplers[firstSlot + i] = sampler->handle; 3220 metalCommandBuffer->needComputeSamplerBind = true; 3221 } 3222 3223 if (metalCommandBuffer->computeSamplerTextures[firstSlot + i] != textureContainer->activeTexture->handle) { 3224 METAL_INTERNAL_TrackTexture( 3225 metalCommandBuffer, 3226 textureContainer->activeTexture); 3227 3228 metalCommandBuffer->computeSamplerTextures[firstSlot + i] = 3229 textureContainer->activeTexture->handle; 3230 3231 metalCommandBuffer->needComputeSamplerBind = true; 3232 } 3233 } 3234} 3235 3236static void METAL_BindComputeStorageTextures( 3237 SDL_GPUCommandBuffer *commandBuffer, 3238 Uint32 firstSlot, 3239 SDL_GPUTexture *const *storageTextures, 3240 Uint32 numBindings) 3241{ 3242 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 3243 MetalTextureContainer *textureContainer; 3244 3245 for (Uint32 i = 0; i < numBindings; i += 1) { 3246 textureContainer = (MetalTextureContainer *)storageTextures[i]; 3247 3248 if (metalCommandBuffer->computeReadOnlyTextures[firstSlot + i] != textureContainer->activeTexture->handle) { 3249 METAL_INTERNAL_TrackTexture( 3250 metalCommandBuffer, 3251 textureContainer->activeTexture); 3252 3253 metalCommandBuffer->computeReadOnlyTextures[firstSlot + i] = 3254 textureContainer->activeTexture->handle; 3255 3256 metalCommandBuffer->needComputeReadOnlyStorageTextureBind = true; 3257 } 3258 } 3259} 3260 3261static void METAL_BindComputeStorageBuffers( 3262 SDL_GPUCommandBuffer *commandBuffer, 3263 Uint32 firstSlot, 3264 SDL_GPUBuffer *const *storageBuffers, 3265 Uint32 numBindings) 3266{ 3267 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 3268 MetalBufferContainer *bufferContainer; 3269 3270 for (Uint32 i = 0; i < numBindings; i += 1) { 3271 bufferContainer = (MetalBufferContainer *)storageBuffers[i]; 3272 3273 if (metalCommandBuffer->computeReadOnlyBuffers[firstSlot + i] != bufferContainer->activeBuffer->handle) { 3274 METAL_INTERNAL_TrackBuffer( 3275 metalCommandBuffer, 3276 bufferContainer->activeBuffer); 3277 3278 metalCommandBuffer->computeReadOnlyBuffers[firstSlot + i] = 3279 bufferContainer->activeBuffer->handle; 3280 3281 metalCommandBuffer->needComputeReadOnlyStorageBufferBind = true; 3282 } 3283 } 3284} 3285 3286static void METAL_PushComputeUniformData( 3287 SDL_GPUCommandBuffer *commandBuffer, 3288 Uint32 slotIndex, 3289 const void *data, 3290 Uint32 length) 3291{ 3292 @autoreleasepool { 3293 METAL_INTERNAL_PushUniformData( 3294 (MetalCommandBuffer *)commandBuffer, 3295 SDL_GPU_SHADERSTAGE_COMPUTE, 3296 slotIndex, 3297 data, 3298 length); 3299 } 3300} 3301 3302static void METAL_DispatchCompute( 3303 SDL_GPUCommandBuffer *commandBuffer, 3304 Uint32 groupcountX, 3305 Uint32 groupcountY, 3306 Uint32 groupcountZ) 3307{ 3308 @autoreleasepool { 3309 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 3310 MTLSize threadgroups = MTLSizeMake(groupcountX, groupcountY, groupcountZ); 3311 MTLSize threadsPerThreadgroup = MTLSizeMake( 3312 metalCommandBuffer->compute_pipeline->threadcountX, 3313 metalCommandBuffer->compute_pipeline->threadcountY, 3314 metalCommandBuffer->compute_pipeline->threadcountZ); 3315 3316 METAL_INTERNAL_BindComputeResources(metalCommandBuffer); 3317 3318 [metalCommandBuffer->computeEncoder 3319 dispatchThreadgroups:threadgroups 3320 threadsPerThreadgroup:threadsPerThreadgroup]; 3321 } 3322} 3323 3324static void METAL_DispatchComputeIndirect( 3325 SDL_GPUCommandBuffer *commandBuffer, 3326 SDL_GPUBuffer *buffer, 3327 Uint32 offset) 3328{ 3329 @autoreleasepool { 3330 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 3331 MetalBuffer *metalBuffer = ((MetalBufferContainer *)buffer)->activeBuffer; 3332 MTLSize threadsPerThreadgroup = MTLSizeMake( 3333 metalCommandBuffer->compute_pipeline->threadcountX, 3334 metalCommandBuffer->compute_pipeline->threadcountY, 3335 metalCommandBuffer->compute_pipeline->threadcountZ); 3336 3337 METAL_INTERNAL_BindComputeResources(metalCommandBuffer); 3338 3339 [metalCommandBuffer->computeEncoder 3340 dispatchThreadgroupsWithIndirectBuffer:metalBuffer->handle 3341 indirectBufferOffset:offset 3342 threadsPerThreadgroup:threadsPerThreadgroup]; 3343 3344 METAL_INTERNAL_TrackBuffer(metalCommandBuffer, metalBuffer); 3345 } 3346} 3347 3348static void METAL_EndComputePass( 3349 SDL_GPUCommandBuffer *commandBuffer) 3350{ 3351 @autoreleasepool { 3352 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 3353 [metalCommandBuffer->computeEncoder endEncoding]; 3354 metalCommandBuffer->computeEncoder = nil; 3355 3356 for (Uint32 i = 0; i < MAX_TEXTURE_SAMPLERS_PER_STAGE; i += 1) { 3357 metalCommandBuffer->computeSamplers[i] = nil; 3358 metalCommandBuffer->computeSamplerTextures[i] = nil; 3359 } 3360 for (Uint32 i = 0; i < MAX_COMPUTE_WRITE_TEXTURES; i += 1) { 3361 metalCommandBuffer->computeReadWriteTextures[i] = nil; 3362 } 3363 for (Uint32 i = 0; i < MAX_COMPUTE_WRITE_BUFFERS; i += 1) { 3364 metalCommandBuffer->computeReadWriteBuffers[i] = nil; 3365 } 3366 for (Uint32 i = 0; i < MAX_STORAGE_TEXTURES_PER_STAGE; i += 1) { 3367 metalCommandBuffer->computeReadOnlyTextures[i] = nil; 3368 } 3369 for (Uint32 i = 0; i < MAX_STORAGE_BUFFERS_PER_STAGE; i += 1) { 3370 metalCommandBuffer->computeReadOnlyBuffers[i] = nil; 3371 } 3372 } 3373} 3374 3375// Fence Cleanup 3376 3377static void METAL_INTERNAL_ReleaseFenceToPool( 3378 MetalRenderer *renderer, 3379 MetalFence *fence) 3380{ 3381 SDL_LockMutex(renderer->fenceLock); 3382 3383 // FIXME: Should this use EXPAND_IF_NEEDED? 3384 if (renderer->availableFenceCount == renderer->availableFenceCapacity) { 3385 renderer->availableFenceCapacity *= 2; 3386 renderer->availableFences = SDL_realloc( 3387 renderer->availableFences, 3388 renderer->availableFenceCapacity * sizeof(MetalFence *)); 3389 } 3390 renderer->availableFences[renderer->availableFenceCount] = fence; 3391 renderer->availableFenceCount += 1; 3392 3393 SDL_UnlockMutex(renderer->fenceLock); 3394} 3395 3396static void METAL_ReleaseFence( 3397 SDL_GPURenderer *driverData, 3398 SDL_GPUFence *fence) 3399{ 3400 MetalFence *metalFence = (MetalFence *)fence; 3401 if (SDL_AtomicDecRef(&metalFence->referenceCount)) { 3402 METAL_INTERNAL_ReleaseFenceToPool( 3403 (MetalRenderer *)driverData, 3404 (MetalFence *)fence); 3405 } 3406} 3407 3408// Cleanup 3409 3410static void METAL_INTERNAL_CleanCommandBuffer( 3411 MetalRenderer *renderer, 3412 MetalCommandBuffer *commandBuffer, 3413 bool cancel) 3414{ 3415 Uint32 i; 3416 3417 // End any active passes 3418 if (commandBuffer->renderEncoder) { 3419 [commandBuffer->renderEncoder endEncoding]; 3420 commandBuffer->renderEncoder = nil; 3421 } 3422 if (commandBuffer->computeEncoder) { 3423 [commandBuffer->computeEncoder endEncoding]; 3424 commandBuffer->computeEncoder = nil; 3425 } 3426 if (commandBuffer->blitEncoder) { 3427 [commandBuffer->blitEncoder endEncoding]; 3428 commandBuffer->blitEncoder = nil; 3429 } 3430 3431 // Uniform buffers are now available 3432 3433 SDL_LockMutex(renderer->acquireUniformBufferLock); 3434 3435 for (i = 0; i < commandBuffer->usedUniformBufferCount; i += 1) { 3436 METAL_INTERNAL_ReturnUniformBufferToPool( 3437 renderer, 3438 commandBuffer->usedUniformBuffers[i]); 3439 } 3440 commandBuffer->usedUniformBufferCount = 0; 3441 3442 SDL_UnlockMutex(renderer->acquireUniformBufferLock); 3443 3444 // Reference Counting 3445 3446 for (i = 0; i < commandBuffer->usedBufferCount; i += 1) { 3447 (void)SDL_AtomicDecRef(&commandBuffer->usedBuffers[i]->referenceCount); 3448 } 3449 commandBuffer->usedBufferCount = 0; 3450 3451 for (i = 0; i < commandBuffer->usedTextureCount; i += 1) { 3452 (void)SDL_AtomicDecRef(&commandBuffer->usedTextures[i]->referenceCount); 3453 } 3454 commandBuffer->usedTextureCount = 0; 3455 3456 // Reset presentation 3457 commandBuffer->windowDataCount = 0; 3458 3459 // Reset bindings 3460 for (i = 0; i < MAX_VERTEX_BUFFERS; i += 1) { 3461 commandBuffer->vertexBuffers[i] = nil; 3462 commandBuffer->vertexBufferOffsets[i] = 0; 3463 } 3464 commandBuffer->vertexBufferCount = 0; 3465 commandBuffer->indexBuffer = NULL; 3466 for (i = 0; i < MAX_TEXTURE_SAMPLERS_PER_STAGE; i += 1) { 3467 commandBuffer->vertexSamplers[i] = nil; 3468 commandBuffer->vertexTextures[i] = nil; 3469 commandBuffer->fragmentSamplers[i] = nil; 3470 commandBuffer->fragmentTextures[i] = nil; 3471 commandBuffer->computeSamplers[i] = nil; 3472 commandBuffer->computeSamplerTextures[i] = nil; 3473 } 3474 for (i = 0; i < MAX_STORAGE_TEXTURES_PER_STAGE; i += 1) { 3475 commandBuffer->vertexStorageTextures[i] = nil; 3476 commandBuffer->fragmentStorageTextures[i] = nil; 3477 commandBuffer->computeReadOnlyTextures[i] = nil; 3478 } 3479 for (i = 0; i < MAX_STORAGE_BUFFERS_PER_STAGE; i += 1) { 3480 commandBuffer->vertexStorageBuffers[i] = nil; 3481 commandBuffer->fragmentStorageBuffers[i] = nil; 3482 commandBuffer->computeReadOnlyBuffers[i] = nil; 3483 } 3484 for (i = 0; i < MAX_COMPUTE_WRITE_TEXTURES; i += 1) { 3485 commandBuffer->computeReadWriteTextures[i] = nil; 3486 } 3487 for (i = 0; i < MAX_COMPUTE_WRITE_BUFFERS; i += 1) { 3488 commandBuffer->computeReadWriteBuffers[i] = nil; 3489 } 3490 3491 commandBuffer->needVertexBufferBind = false; 3492 commandBuffer->needVertexSamplerBind = false; 3493 commandBuffer->needVertexStorageBufferBind = false; 3494 commandBuffer->needVertexStorageTextureBind = false; 3495 SDL_zeroa(commandBuffer->needVertexUniformBufferBind); 3496 3497 commandBuffer->needFragmentSamplerBind = false; 3498 commandBuffer->needFragmentStorageBufferBind = false; 3499 commandBuffer->needFragmentStorageTextureBind = false; 3500 SDL_zeroa(commandBuffer->needFragmentUniformBufferBind); 3501 3502 commandBuffer->needComputeSamplerBind = false; 3503 commandBuffer->needComputeReadOnlyStorageBufferBind = false; 3504 commandBuffer->needComputeReadOnlyStorageTextureBind = false; 3505 SDL_zeroa(commandBuffer->needComputeUniformBufferBind); 3506 3507 // The fence is now available (unless SubmitAndAcquireFence was called) 3508 if (commandBuffer->autoReleaseFence) { 3509 METAL_ReleaseFence( 3510 (SDL_GPURenderer *)renderer, 3511 (SDL_GPUFence *)commandBuffer->fence); 3512 } 3513 3514 // Return command buffer to pool 3515 SDL_LockMutex(renderer->acquireCommandBufferLock); 3516 // FIXME: Should this use EXPAND_IF_NEEDED? 3517 if (renderer->availableCommandBufferCount == renderer->availableCommandBufferCapacity) { 3518 renderer->availableCommandBufferCapacity += 1; 3519 renderer->availableCommandBuffers = SDL_realloc( 3520 renderer->availableCommandBuffers, 3521 renderer->availableCommandBufferCapacity * sizeof(MetalCommandBuffer *)); 3522 } 3523 renderer->availableCommandBuffers[renderer->availableCommandBufferCount] = commandBuffer; 3524 renderer->availableCommandBufferCount += 1; 3525 SDL_UnlockMutex(renderer->acquireCommandBufferLock); 3526 3527 // Remove this command buffer from the submitted list 3528 if (!cancel) { 3529 for (i = 0; i < renderer->submittedCommandBufferCount; i += 1) { 3530 if (renderer->submittedCommandBuffers[i] == commandBuffer) { 3531 renderer->submittedCommandBuffers[i] = renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount - 1]; 3532 renderer->submittedCommandBufferCount -= 1; 3533 } 3534 } 3535 } 3536} 3537 3538// This function assumes that it's called from within an autorelease pool 3539static void METAL_INTERNAL_PerformPendingDestroys( 3540 MetalRenderer *renderer) 3541{ 3542 Sint32 referenceCount = 0; 3543 Sint32 i; 3544 Uint32 j; 3545 3546 for (i = renderer->bufferContainersToDestroyCount - 1; i >= 0; i -= 1) { 3547 referenceCount = 0; 3548 for (j = 0; j < renderer->bufferContainersToDestroy[i]->bufferCount; j += 1) { 3549 referenceCount += SDL_GetAtomicInt(&renderer->bufferContainersToDestroy[i]->buffers[j]->referenceCount); 3550 } 3551 3552 if (referenceCount == 0) { 3553 METAL_INTERNAL_DestroyBufferContainer( 3554 renderer->bufferContainersToDestroy[i]); 3555 3556 renderer->bufferContainersToDestroy[i] = renderer->bufferContainersToDestroy[renderer->bufferContainersToDestroyCount - 1]; 3557 renderer->bufferContainersToDestroyCount -= 1; 3558 } 3559 } 3560 3561 for (i = renderer->textureContainersToDestroyCount - 1; i >= 0; i -= 1) { 3562 referenceCount = 0; 3563 for (j = 0; j < renderer->textureContainersToDestroy[i]->textureCount; j += 1) { 3564 referenceCount += SDL_GetAtomicInt(&renderer->textureContainersToDestroy[i]->textures[j]->referenceCount); 3565 } 3566 3567 if (referenceCount == 0) { 3568 METAL_INTERNAL_DestroyTextureContainer( 3569 renderer->textureContainersToDestroy[i]); 3570 3571 renderer->textureContainersToDestroy[i] = renderer->textureContainersToDestroy[renderer->textureContainersToDestroyCount - 1]; 3572 renderer->textureContainersToDestroyCount -= 1; 3573 } 3574 } 3575} 3576 3577// Fences 3578 3579static bool METAL_WaitForFences( 3580 SDL_GPURenderer *driverData, 3581 bool waitAll, 3582 SDL_GPUFence *const *fences, 3583 Uint32 numFences) 3584{ 3585 @autoreleasepool { 3586 MetalRenderer *renderer = (MetalRenderer *)driverData; 3587 bool waiting; 3588 3589 if (waitAll) { 3590 for (Uint32 i = 0; i < numFences; i += 1) { 3591 while (!SDL_GetAtomicInt(&((MetalFence *)fences[i])->complete)) { 3592 // Spin! 3593 } 3594 } 3595 } else { 3596 waiting = 1; 3597 while (waiting) { 3598 for (Uint32 i = 0; i < numFences; i += 1) { 3599 if (SDL_GetAtomicInt(&((MetalFence *)fences[i])->complete) > 0) { 3600 waiting = 0; 3601 break; 3602 } 3603 } 3604 } 3605 } 3606 3607 METAL_INTERNAL_PerformPendingDestroys(renderer); 3608 3609 return true; 3610 } 3611} 3612 3613static bool METAL_QueryFence( 3614 SDL_GPURenderer *driverData, 3615 SDL_GPUFence *fence) 3616{ 3617 MetalFence *metalFence = (MetalFence *)fence; 3618 return SDL_GetAtomicInt(&metalFence->complete) == 1; 3619} 3620 3621// Window and Swapchain Management 3622 3623static MetalWindowData *METAL_INTERNAL_FetchWindowData(SDL_Window *window) 3624{ 3625 SDL_PropertiesID properties = SDL_GetWindowProperties(window); 3626 return (MetalWindowData *)SDL_GetPointerProperty(properties, WINDOW_PROPERTY_DATA, NULL); 3627} 3628 3629static bool METAL_SupportsSwapchainComposition( 3630 SDL_GPURenderer *driverData, 3631 SDL_Window *window, 3632 SDL_GPUSwapchainComposition swapchainComposition) 3633{ 3634#ifndef SDL_PLATFORM_MACOS 3635 if (swapchainComposition == SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2084) { 3636 return false; 3637 } 3638#endif 3639 3640 if (@available(macOS 11.0, *)) { 3641 return true; 3642 } else { 3643 return swapchainComposition != SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2084; 3644 } 3645} 3646 3647// This function assumes that it's called from within an autorelease pool 3648static Uint8 METAL_INTERNAL_CreateSwapchain( 3649 MetalRenderer *renderer, 3650 MetalWindowData *windowData, 3651 SDL_GPUSwapchainComposition swapchainComposition, 3652 SDL_GPUPresentMode presentMode) 3653{ 3654 CGColorSpaceRef colorspace; 3655 CGSize drawableSize; 3656 3657 windowData->view = SDL_Metal_CreateView(windowData->window); 3658 windowData->drawable = nil; 3659 windowData->presentMode = SDL_GPU_PRESENTMODE_VSYNC; 3660 windowData->frameCounter = 0; 3661 3662 for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) { 3663 windowData->inFlightFences[i] = NULL; 3664 } 3665 3666 windowData->layer = (__bridge CAMetalLayer *)(SDL_Metal_GetLayer(windowData->view)); 3667 windowData->layer.device = renderer->device; 3668#ifdef SDL_PLATFORM_MACOS 3669 if (@available(macOS 10.13, *)) { 3670 windowData->layer.displaySyncEnabled = (presentMode != SDL_GPU_PRESENTMODE_IMMEDIATE); 3671 windowData->presentMode = presentMode; 3672 } 3673#endif 3674 windowData->layer.pixelFormat = SDLToMetal_TextureFormat(SwapchainCompositionToFormat[swapchainComposition]); 3675#ifndef SDL_PLATFORM_TVOS 3676 if (@available(iOS 16.0, *)) { 3677 windowData->layer.wantsExtendedDynamicRangeContent = (swapchainComposition != SDL_GPU_SWAPCHAINCOMPOSITION_SDR); 3678 } 3679#endif 3680 3681 colorspace = CGColorSpaceCreateWithName(SwapchainCompositionToColorSpace[swapchainComposition]); 3682 windowData->layer.colorspace = colorspace; 3683 CGColorSpaceRelease(colorspace); 3684 3685 windowData->texture.handle = nil; // This will be set in AcquireSwapchainTexture. 3686 3687 // Precache blit pipelines for the swapchain format 3688 for (Uint32 i = 0; i < 4; i += 1) { 3689 SDL_GPU_FetchBlitPipeline( 3690 renderer->sdlGPUDevice, 3691 (SDL_GPUTextureType)i, 3692 SwapchainCompositionToFormat[swapchainComposition], 3693 renderer->blitVertexShader, 3694 renderer->blitFrom2DShader, 3695 renderer->blitFrom2DArrayShader, 3696 renderer->blitFrom3DShader, 3697 renderer->blitFromCubeShader, 3698 renderer->blitFromCubeArrayShader, 3699 &renderer->blitPipelines, 3700 &renderer->blitPipelineCount, 3701 &renderer->blitPipelineCapacity); 3702 } 3703 3704 // Set up the texture container 3705 SDL_zero(windowData->textureContainer); 3706 windowData->textureContainer.canBeCycled = 0; 3707 windowData->textureContainer.activeTexture = &windowData->texture; 3708 windowData->textureContainer.textureCapacity = 1; 3709 windowData->textureContainer.textureCount = 1; 3710 windowData->textureContainer.header.info.format = SwapchainCompositionToFormat[swapchainComposition]; 3711 windowData->textureContainer.header.info.num_levels = 1; 3712 windowData->textureContainer.header.info.layer_count_or_depth = 1; 3713 windowData->textureContainer.header.info.type = SDL_GPU_TEXTURETYPE_2D; 3714 windowData->textureContainer.header.info.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; 3715 3716 drawableSize = windowData->layer.drawableSize; 3717 windowData->textureContainer.header.info.width = (Uint32)drawableSize.width; 3718 windowData->textureContainer.header.info.height = (Uint32)drawableSize.height; 3719 3720 return 1; 3721} 3722 3723static bool METAL_SupportsPresentMode( 3724 SDL_GPURenderer *driverData, 3725 SDL_Window *window, 3726 SDL_GPUPresentMode presentMode) 3727{ 3728 switch (presentMode) { 3729#ifdef SDL_PLATFORM_MACOS 3730 case SDL_GPU_PRESENTMODE_IMMEDIATE: 3731#endif 3732 case SDL_GPU_PRESENTMODE_VSYNC: 3733 return true; 3734 default: 3735 return false; 3736 } 3737} 3738 3739static bool METAL_ClaimWindow( 3740 SDL_GPURenderer *driverData, 3741 SDL_Window *window) 3742{ 3743 @autoreleasepool { 3744 MetalRenderer *renderer = (MetalRenderer *)driverData; 3745 MetalWindowData *windowData = METAL_INTERNAL_FetchWindowData(window); 3746 3747 if (windowData == NULL) { 3748 windowData = (MetalWindowData *)SDL_calloc(1, sizeof(MetalWindowData)); 3749 windowData->window = window; 3750 3751 if (METAL_INTERNAL_CreateSwapchain(renderer, windowData, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_VSYNC)) { 3752 SDL_SetPointerProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA, windowData); 3753 3754 SDL_LockMutex(renderer->windowLock); 3755 3756 if (renderer->claimedWindowCount >= renderer->claimedWindowCapacity) { 3757 renderer->claimedWindowCapacity *= 2; 3758 renderer->claimedWindows = SDL_realloc( 3759 renderer->claimedWindows, 3760 renderer->claimedWindowCapacity * sizeof(MetalWindowData *)); 3761 } 3762 renderer->claimedWindows[renderer->claimedWindowCount] = windowData; 3763 renderer->claimedWindowCount += 1; 3764 3765 SDL_UnlockMutex(renderer->windowLock); 3766 3767 return true; 3768 } else { 3769 SDL_free(windowData); 3770 SET_STRING_ERROR_AND_RETURN("Could not create swapchain, failed to claim window", false); 3771 } 3772 } else { 3773 SET_ERROR_AND_RETURN("%s", "Window already claimed!", false); 3774 } 3775 } 3776} 3777 3778static void METAL_ReleaseWindow( 3779 SDL_GPURenderer *driverData, 3780 SDL_Window *window) 3781{ 3782 @autoreleasepool { 3783 MetalRenderer *renderer = (MetalRenderer *)driverData; 3784 MetalWindowData *windowData = METAL_INTERNAL_FetchWindowData(window); 3785 3786 if (windowData == NULL) { 3787 SET_STRING_ERROR_AND_RETURN("Window is not claimed by this SDL_GPUDevice", ); 3788 } 3789 3790 METAL_Wait(driverData); 3791 SDL_Metal_DestroyView(windowData->view); 3792 for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) { 3793 if (windowData->inFlightFences[i] != NULL) { 3794 METAL_ReleaseFence( 3795 (SDL_GPURenderer *)renderer, 3796 windowData->inFlightFences[i]); 3797 } 3798 } 3799 3800 SDL_LockMutex(renderer->windowLock); 3801 for (Uint32 i = 0; i < renderer->claimedWindowCount; i += 1) { 3802 if (renderer->claimedWindows[i]->window == window) { 3803 renderer->claimedWindows[i] = renderer->claimedWindows[renderer->claimedWindowCount - 1]; 3804 renderer->claimedWindowCount -= 1; 3805 break; 3806 } 3807 } 3808 SDL_UnlockMutex(renderer->windowLock); 3809 3810 SDL_free(windowData); 3811 3812 SDL_ClearProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA); 3813 } 3814} 3815 3816static bool METAL_WaitForSwapchain( 3817 SDL_GPURenderer *driverData, 3818 SDL_Window *window) 3819{ 3820 @autoreleasepool { 3821 MetalRenderer *renderer = (MetalRenderer *)driverData; 3822 MetalWindowData *windowData = METAL_INTERNAL_FetchWindowData(window); 3823 3824 if (windowData == NULL) { 3825 SET_STRING_ERROR_AND_RETURN("Cannot wait for a swapchain from an unclaimed window!", false); 3826 } 3827 3828 if (windowData->inFlightFences[windowData->frameCounter] != NULL) { 3829 if (!METAL_WaitForFences( 3830 driverData, 3831 true, 3832 &windowData->inFlightFences[windowData->frameCounter], 3833 1)) { 3834 return false; 3835 } 3836 } 3837 3838 return true; 3839 } 3840} 3841 3842static bool METAL_INTERNAL_AcquireSwapchainTexture( 3843 bool block, 3844 SDL_GPUCommandBuffer *commandBuffer, 3845 SDL_Window *window, 3846 SDL_GPUTexture **texture, 3847 Uint32 *swapchainTextureWidth, 3848 Uint32 *swapchainTextureHeight) 3849{ 3850 @autoreleasepool { 3851 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 3852 MetalRenderer *renderer = metalCommandBuffer->renderer; 3853 MetalWindowData *windowData; 3854 CGSize drawableSize; 3855 3856 *texture = NULL; 3857 if (swapchainTextureWidth) { 3858 *swapchainTextureWidth = 0; 3859 } 3860 if (swapchainTextureHeight) { 3861 *swapchainTextureHeight = 0; 3862 } 3863 3864 windowData = METAL_INTERNAL_FetchWindowData(window); 3865 if (windowData == NULL) { 3866 SET_STRING_ERROR_AND_RETURN("Window is not claimed by this SDL_GPUDevice", false); 3867 } 3868 3869 // Update the window size 3870 drawableSize = windowData->layer.drawableSize; 3871 windowData->textureContainer.header.info.width = (Uint32)drawableSize.width; 3872 windowData->textureContainer.header.info.height = (Uint32)drawableSize.height; 3873 if (swapchainTextureWidth) { 3874 *swapchainTextureWidth = (Uint32)drawableSize.width; 3875 } 3876 if (swapchainTextureHeight) { 3877 *swapchainTextureHeight = (Uint32)drawableSize.height; 3878 } 3879 3880 if (windowData->inFlightFences[windowData->frameCounter] != NULL) { 3881 if (block) { 3882 // If we are blocking, just wait for the fence! 3883 if (!METAL_WaitForFences( 3884 (SDL_GPURenderer *)renderer, 3885 true, 3886 &windowData->inFlightFences[windowData->frameCounter], 3887 1)) { 3888 return false; 3889 } 3890 } else { 3891 // If we are not blocking and the least recent fence is not signaled, 3892 // return true to indicate that there is no error but rendering should be skipped. 3893 if (!METAL_QueryFence( 3894 (SDL_GPURenderer *)metalCommandBuffer->renderer, 3895 windowData->inFlightFences[windowData->frameCounter])) { 3896 return true; 3897 } 3898 } 3899 3900 METAL_ReleaseFence( 3901 (SDL_GPURenderer *)metalCommandBuffer->renderer, 3902 windowData->inFlightFences[windowData->frameCounter]); 3903 3904 windowData->inFlightFences[windowData->frameCounter] = NULL; 3905 } 3906 3907 // Get the drawable and its underlying texture 3908 windowData->drawable = [windowData->layer nextDrawable]; 3909 windowData->texture.handle = [windowData->drawable texture]; 3910 3911 // Set up presentation 3912 if (metalCommandBuffer->windowDataCount == metalCommandBuffer->windowDataCapacity) { 3913 metalCommandBuffer->windowDataCapacity += 1; 3914 metalCommandBuffer->windowDatas = SDL_realloc( 3915 metalCommandBuffer->windowDatas, 3916 metalCommandBuffer->windowDataCapacity * sizeof(MetalWindowData *)); 3917 } 3918 metalCommandBuffer->windowDatas[metalCommandBuffer->windowDataCount] = windowData; 3919 metalCommandBuffer->windowDataCount += 1; 3920 3921 // Return the swapchain texture 3922 *texture = (SDL_GPUTexture *)&windowData->textureContainer; 3923 return true; 3924 } 3925} 3926 3927static bool METAL_AcquireSwapchainTexture( 3928 SDL_GPUCommandBuffer *command_buffer, 3929 SDL_Window *window, 3930 SDL_GPUTexture **swapchain_texture, 3931 Uint32 *swapchain_texture_width, 3932 Uint32 *swapchain_texture_height 3933) { 3934 return METAL_INTERNAL_AcquireSwapchainTexture( 3935 false, 3936 command_buffer, 3937 window, 3938 swapchain_texture, 3939 swapchain_texture_width, 3940 swapchain_texture_height); 3941} 3942 3943static bool METAL_WaitAndAcquireSwapchainTexture( 3944 SDL_GPUCommandBuffer *command_buffer, 3945 SDL_Window *window, 3946 SDL_GPUTexture **swapchain_texture, 3947 Uint32 *swapchain_texture_width, 3948 Uint32 *swapchain_texture_height 3949) { 3950 return METAL_INTERNAL_AcquireSwapchainTexture( 3951 true, 3952 command_buffer, 3953 window, 3954 swapchain_texture, 3955 swapchain_texture_width, 3956 swapchain_texture_height); 3957} 3958 3959static SDL_GPUTextureFormat METAL_GetSwapchainTextureFormat( 3960 SDL_GPURenderer *driverData, 3961 SDL_Window *window) 3962{ 3963 MetalRenderer *renderer = (MetalRenderer *)driverData; 3964 MetalWindowData *windowData = METAL_INTERNAL_FetchWindowData(window); 3965 3966 if (windowData == NULL) { 3967 SET_STRING_ERROR_AND_RETURN("Cannot get swapchain format, window has not been claimed", SDL_GPU_TEXTUREFORMAT_INVALID); 3968 } 3969 3970 return windowData->textureContainer.header.info.format; 3971} 3972 3973static bool METAL_SetSwapchainParameters( 3974 SDL_GPURenderer *driverData, 3975 SDL_Window *window, 3976 SDL_GPUSwapchainComposition swapchainComposition, 3977 SDL_GPUPresentMode presentMode) 3978{ 3979 @autoreleasepool { 3980 MetalRenderer *renderer = (MetalRenderer *)driverData; 3981 MetalWindowData *windowData = METAL_INTERNAL_FetchWindowData(window); 3982 CGColorSpaceRef colorspace; 3983 3984 if (windowData == NULL) { 3985 SET_STRING_ERROR_AND_RETURN("Cannot set swapchain parameters, window has not been claimed!", false); 3986 } 3987 3988 if (!METAL_SupportsSwapchainComposition(driverData, window, swapchainComposition)) { 3989 SET_STRING_ERROR_AND_RETURN("Swapchain composition not supported", false); 3990 } 3991 3992 if (!METAL_SupportsPresentMode(driverData, window, presentMode)) { 3993 SET_STRING_ERROR_AND_RETURN("Present mode not supported", false); 3994 } 3995 3996 METAL_Wait(driverData); 3997 3998 windowData->presentMode = SDL_GPU_PRESENTMODE_VSYNC; 3999 4000#ifdef SDL_PLATFORM_MACOS 4001 if (@available(macOS 10.13, *)) { 4002 windowData->layer.displaySyncEnabled = (presentMode != SDL_GPU_PRESENTMODE_IMMEDIATE); 4003 windowData->presentMode = presentMode; 4004 } 4005#endif 4006 windowData->layer.pixelFormat = SDLToMetal_TextureFormat(SwapchainCompositionToFormat[swapchainComposition]); 4007#ifndef SDL_PLATFORM_TVOS 4008 if (@available(iOS 16.0, *)) { 4009 windowData->layer.wantsExtendedDynamicRangeContent = (swapchainComposition != SDL_GPU_SWAPCHAINCOMPOSITION_SDR); 4010 } 4011#endif 4012 4013 colorspace = CGColorSpaceCreateWithName(SwapchainCompositionToColorSpace[swapchainComposition]); 4014 windowData->layer.colorspace = colorspace; 4015 CGColorSpaceRelease(colorspace); 4016 4017 windowData->textureContainer.header.info.format = SwapchainCompositionToFormat[swapchainComposition]; 4018 4019 return true; 4020 } 4021} 4022 4023static bool METAL_SetAllowedFramesInFlight( 4024 SDL_GPURenderer *driverData, 4025 Uint32 allowedFramesInFlight) 4026{ 4027 @autoreleasepool { 4028 MetalRenderer *renderer = (MetalRenderer *)driverData; 4029 4030 if (!METAL_Wait(driverData)) { 4031 return false; 4032 } 4033 4034 renderer->allowedFramesInFlight = allowedFramesInFlight; 4035 return true; 4036 } 4037} 4038 4039// Submission 4040 4041static bool METAL_Submit( 4042 SDL_GPUCommandBuffer *commandBuffer) 4043{ 4044 @autoreleasepool { 4045 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 4046 MetalRenderer *renderer = metalCommandBuffer->renderer; 4047 4048 SDL_LockMutex(renderer->submitLock); 4049 4050 if (!METAL_INTERNAL_AcquireFence(renderer, metalCommandBuffer)) { 4051 SDL_UnlockMutex(renderer->submitLock); 4052 return false; 4053 } 4054 4055 // Enqueue present requests, if applicable 4056 for (Uint32 i = 0; i < metalCommandBuffer->windowDataCount; i += 1) { 4057 MetalWindowData *windowData = metalCommandBuffer->windowDatas[i]; 4058 [metalCommandBuffer->handle presentDrawable:windowData->drawable]; 4059 windowData->drawable = nil; 4060 4061 windowData->inFlightFences[windowData->frameCounter] = (SDL_GPUFence *)metalCommandBuffer->fence; 4062 4063 (void)SDL_AtomicIncRef(&metalCommandBuffer->fence->referenceCount); 4064 4065 windowData->frameCounter = (windowData->frameCounter + 1) % renderer->allowedFramesInFlight; 4066 } 4067 4068 // Notify the fence when the command buffer has completed 4069 [metalCommandBuffer->handle addCompletedHandler:^(id<MTLCommandBuffer> buffer) { 4070 SDL_AtomicIncRef(&metalCommandBuffer->fence->complete); 4071 }]; 4072 4073 // Submit the command buffer 4074 [metalCommandBuffer->handle commit]; 4075 metalCommandBuffer->handle = nil; 4076 4077 // Mark the command buffer as submitted 4078 if (renderer->submittedCommandBufferCount >= renderer->submittedCommandBufferCapacity) { 4079 renderer->submittedCommandBufferCapacity = renderer->submittedCommandBufferCount + 1; 4080 4081 renderer->submittedCommandBuffers = SDL_realloc( 4082 renderer->submittedCommandBuffers, 4083 sizeof(MetalCommandBuffer *) * renderer->submittedCommandBufferCapacity); 4084 } 4085 renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount] = metalCommandBuffer; 4086 renderer->submittedCommandBufferCount += 1; 4087 4088 // Check if we can perform any cleanups 4089 for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) { 4090 if (SDL_GetAtomicInt(&renderer->submittedCommandBuffers[i]->fence->complete)) { 4091 METAL_INTERNAL_CleanCommandBuffer( 4092 renderer, 4093 renderer->submittedCommandBuffers[i], 4094 false); 4095 } 4096 } 4097 4098 METAL_INTERNAL_PerformPendingDestroys(renderer); 4099 4100 SDL_UnlockMutex(renderer->submitLock); 4101 4102 return true; 4103 } 4104} 4105 4106static SDL_GPUFence *METAL_SubmitAndAcquireFence( 4107 SDL_GPUCommandBuffer *commandBuffer) 4108{ 4109 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 4110 metalCommandBuffer->autoReleaseFence = false; 4111 if (!METAL_Submit(commandBuffer)) { 4112 return NULL; 4113 } 4114 return (SDL_GPUFence *)metalCommandBuffer->fence; 4115} 4116 4117static bool METAL_Cancel( 4118 SDL_GPUCommandBuffer *commandBuffer) 4119{ 4120 MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer; 4121 MetalRenderer *renderer = metalCommandBuffer->renderer; 4122 4123 metalCommandBuffer->autoReleaseFence = false; 4124 SDL_LockMutex(renderer->submitLock); 4125 METAL_INTERNAL_CleanCommandBuffer(renderer, metalCommandBuffer, true); 4126 SDL_UnlockMutex(renderer->submitLock); 4127 4128 return true; 4129} 4130 4131static bool METAL_Wait( 4132 SDL_GPURenderer *driverData) 4133{ 4134 @autoreleasepool { 4135 MetalRenderer *renderer = (MetalRenderer *)driverData; 4136 MetalCommandBuffer *commandBuffer; 4137 4138 /* 4139 * Wait for all submitted command buffers to complete. 4140 * Sort of equivalent to vkDeviceWaitIdle. 4141 */ 4142 for (Uint32 i = 0; i < renderer->submittedCommandBufferCount; i += 1) { 4143 while (!SDL_GetAtomicInt(&renderer->submittedCommandBuffers[i]->fence->complete)) { 4144 // Spin! 4145 } 4146 } 4147 4148 SDL_LockMutex(renderer->submitLock); 4149 4150 for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) { 4151 commandBuffer = renderer->submittedCommandBuffers[i]; 4152 METAL_INTERNAL_CleanCommandBuffer(renderer, commandBuffer, false); 4153 } 4154 4155 METAL_INTERNAL_PerformPendingDestroys(renderer); 4156 4157 SDL_UnlockMutex(renderer->submitLock); 4158 4159 return true; 4160 } 4161} 4162 4163// Format Info 4164 4165// FIXME: Check simultaneous read-write support 4166static bool METAL_SupportsTextureFormat( 4167 SDL_GPURenderer *driverData, 4168 SDL_GPUTextureFormat format, 4169 SDL_GPUTextureType type, 4170 SDL_GPUTextureUsageFlags usage) 4171{ 4172 @autoreleasepool { 4173 MetalRenderer *renderer = (MetalRenderer *)driverData; 4174 4175 // Only depth textures can be used as... depth textures 4176 if ((usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET)) { 4177 if (!IsDepthFormat(format)) { 4178 return false; 4179 } 4180 } 4181 4182 // Cube arrays are not supported on older iOS devices 4183 if (type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) { 4184#ifdef SDL_PLATFORM_MACOS 4185 return true; 4186#else 4187 if (@available(iOS 13.0, tvOS 13.0, *)) { 4188 if (!([renderer->device supportsFamily:MTLGPUFamilyCommon2] || 4189 [renderer->device supportsFamily:MTLGPUFamilyApple4])) { 4190 return false; 4191 } 4192 } else { 4193 return false; 4194 } 4195#endif 4196 } 4197 4198 switch (format) { 4199 // Apple GPU exclusive 4200 case SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM: 4201 case SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM: 4202 case SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM: 4203 if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) { 4204 return [renderer->device supportsFamily:MTLGPUFamilyApple1]; 4205 } else { 4206 return false; 4207 } 4208 4209 // Requires BC compression support 4210 case SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM: 4211 case SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM: 4212 case SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM: 4213 case SDL_GPU_TEXTUREFORMAT_BC4_R_UNORM: 4214 case SDL_GPU_TEXTUREFORMAT_BC5_RG_UNORM: 4215 case SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM: 4216 case SDL_GPU_TEXTUREFORMAT_BC6H_RGB_FLOAT: 4217 case SDL_GPU_TEXTUREFORMAT_BC6H_RGB_UFLOAT: 4218 case SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM_SRGB: 4219 case SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM_SRGB: 4220 case SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM_SRGB: 4221 case SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM_SRGB: 4222 if (@available(iOS 16.4, tvOS 16.4, *)) { 4223 if (usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) { 4224 return false; 4225 } 4226 if (@available(macOS 11.0, *)) { 4227 return [renderer->device supportsBCTextureCompression]; 4228 } else { 4229 return true; 4230 } 4231 } else { 4232 return false; 4233 } 4234 4235 // Requires D24S8 support 4236 case SDL_GPU_TEXTUREFORMAT_D24_UNORM: 4237 case SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT: 4238#ifdef SDL_PLATFORM_MACOS 4239 return [renderer->device isDepth24Stencil8PixelFormatSupported]; 4240#else 4241 return false; 4242#endif 4243 4244 case SDL_GPU_TEXTUREFORMAT_D16_UNORM: 4245 if (@available(macOS 10.12, iOS 13.0, tvOS 13.0, *)) { 4246 return true; 4247 } else { 4248 return false; 4249 } 4250 4251 case SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM: 4252 case SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM: 4253 case SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM: 4254 case SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM: 4255 case SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM: 4256 case SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM: 4257 case SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM: 4258 case SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM: 4259 case SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM: 4260 case SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM: 4261 case SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM: 4262 case SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM: 4263 case SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM: 4264 case SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM: 4265 case SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM_SRGB: 4266 case SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM_SRGB: 4267 case SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM_SRGB: 4268 case SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM_SRGB: 4269 case SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM_SRGB: 4270 case SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM_SRGB: 4271 case SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM_SRGB: 4272 case SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM_SRGB: 4273 case SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM_SRGB: 4274 case SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM_SRGB: 4275 case SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM_SRGB: 4276 case SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM_SRGB: 4277 case SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM_SRGB: 4278 case SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM_SRGB: 4279#ifdef SDL_PLATFORM_MACOS 4280 if (@available(macOS 11.0, *)) { 4281 return [renderer->device supportsFamily:MTLGPUFamilyApple7]; 4282 } else { 4283 return false; 4284 } 4285#else 4286 return true; 4287#endif 4288 case SDL_GPU_TEXTUREFORMAT_ASTC_4x4_FLOAT: 4289 case SDL_GPU_TEXTUREFORMAT_ASTC_5x4_FLOAT: 4290 case SDL_GPU_TEXTUREFORMAT_ASTC_5x5_FLOAT: 4291 case SDL_GPU_TEXTUREFORMAT_ASTC_6x5_FLOAT: 4292 case SDL_GPU_TEXTUREFORMAT_ASTC_6x6_FLOAT: 4293 case SDL_GPU_TEXTUREFORMAT_ASTC_8x5_FLOAT: 4294 case SDL_GPU_TEXTUREFORMAT_ASTC_8x6_FLOAT: 4295 case SDL_GPU_TEXTUREFORMAT_ASTC_8x8_FLOAT: 4296 case SDL_GPU_TEXTUREFORMAT_ASTC_10x5_FLOAT: 4297 case SDL_GPU_TEXTUREFORMAT_ASTC_10x6_FLOAT: 4298 case SDL_GPU_TEXTUREFORMAT_ASTC_10x8_FLOAT: 4299 case SDL_GPU_TEXTUREFORMAT_ASTC_10x10_FLOAT: 4300 case SDL_GPU_TEXTUREFORMAT_ASTC_12x10_FLOAT: 4301 case SDL_GPU_TEXTUREFORMAT_ASTC_12x12_FLOAT: 4302#ifdef SDL_PLATFORM_MACOS 4303 if (@available(macOS 11.0, *)) { 4304 return [renderer->device supportsFamily:MTLGPUFamilyApple7]; 4305 } else { 4306 return false; 4307 } 4308#else 4309 if (@available(iOS 13.0, tvOS 13.0, *)) { 4310 return [renderer->device supportsFamily:MTLGPUFamilyApple6]; 4311 } else { 4312 return false; 4313 } 4314#endif 4315 default: 4316 return true; 4317 } 4318 } 4319} 4320 4321// Device Creation 4322 4323static bool METAL_PrepareDriver(SDL_VideoDevice *this, SDL_PropertiesID props) 4324{ 4325 if (!SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN, false) && 4326 !SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_METALLIB_BOOLEAN, false)) { 4327 return false; 4328 } 4329 4330 if (@available(macOS 10.14, iOS 13.0, tvOS 13.0, *)) { 4331 return (this->Metal_CreateView != NULL); 4332 } 4333 return false; 4334} 4335 4336static void METAL_INTERNAL_InitBlitResources( 4337 MetalRenderer *renderer) 4338{ 4339 SDL_GPUShaderCreateInfo shaderModuleCreateInfo; 4340 SDL_GPUSamplerCreateInfo createinfo; 4341 4342 // Allocate the dynamic blit pipeline list 4343 renderer->blitPipelineCapacity = 2; 4344 renderer->blitPipelineCount = 0; 4345 renderer->blitPipelines = SDL_calloc( 4346 renderer->blitPipelineCapacity, sizeof(BlitPipelineCacheEntry)); 4347 4348 // Fullscreen vertex shader 4349 SDL_zero(shaderModuleCreateInfo); 4350 shaderModuleCreateInfo.code = FullscreenVert_metallib; 4351 shaderModuleCreateInfo.code_size = FullscreenVert_metallib_len; 4352 shaderModuleCreateInfo.stage = SDL_GPU_SHADERSTAGE_VERTEX; 4353 shaderModuleCreateInfo.format = SDL_GPU_SHADERFORMAT_METALLIB; 4354 shaderModuleCreateInfo.entrypoint = "FullscreenVert"; 4355 4356 renderer->blitVertexShader = METAL_CreateShader( 4357 (SDL_GPURenderer *)renderer, 4358 &shaderModuleCreateInfo); 4359 4360 if (renderer->blitVertexShader == NULL) { 4361 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile vertex shader for blit!"); 4362 } 4363 4364 // BlitFrom2D fragment shader 4365 shaderModuleCreateInfo.code = BlitFrom2D_metallib; 4366 shaderModuleCreateInfo.code_size = BlitFrom2D_metallib_len; 4367 shaderModuleCreateInfo.stage = SDL_GPU_SHADERSTAGE_FRAGMENT; 4368 shaderModuleCreateInfo.entrypoint = "BlitFrom2D"; 4369 shaderModuleCreateInfo.num_samplers = 1; 4370 shaderModuleCreateInfo.num_uniform_buffers = 1; 4371 4372 renderer->blitFrom2DShader = METAL_CreateShader( 4373 (SDL_GPURenderer *)renderer, 4374 &shaderModuleCreateInfo); 4375 4376 if (renderer->blitFrom2DShader == NULL) { 4377 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFrom2D fragment shader!"); 4378 } 4379 4380 // BlitFrom2DArray fragment shader 4381 shaderModuleCreateInfo.code = BlitFrom2DArray_metallib; 4382 shaderModuleCreateInfo.code_size = BlitFrom2DArray_metallib_len; 4383 shaderModuleCreateInfo.entrypoint = "BlitFrom2DArray"; 4384 4385 renderer->blitFrom2DArrayShader = METAL_CreateShader( 4386 (SDL_GPURenderer *)renderer, 4387 &shaderModuleCreateInfo); 4388 4389 if (renderer->blitFrom2DArrayShader == NULL) { 4390 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFrom2DArray fragment shader!"); 4391 } 4392 4393 // BlitFrom3D fragment shader 4394 shaderModuleCreateInfo.code = BlitFrom3D_metallib; 4395 shaderModuleCreateInfo.code_size = BlitFrom3D_metallib_len; 4396 shaderModuleCreateInfo.entrypoint = "BlitFrom3D"; 4397 4398 renderer->blitFrom3DShader = METAL_CreateShader( 4399 (SDL_GPURenderer *)renderer, 4400 &shaderModuleCreateInfo); 4401 4402 if (renderer->blitFrom3DShader == NULL) { 4403 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFrom3D fragment shader!"); 4404 } 4405 4406 // BlitFromCube fragment shader 4407 shaderModuleCreateInfo.code = BlitFromCube_metallib; 4408 shaderModuleCreateInfo.code_size = BlitFromCube_metallib_len; 4409 shaderModuleCreateInfo.entrypoint = "BlitFromCube"; 4410 4411 renderer->blitFromCubeShader = METAL_CreateShader( 4412 (SDL_GPURenderer *)renderer, 4413 &shaderModuleCreateInfo); 4414 4415 if (renderer->blitFromCubeShader == NULL) { 4416 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFromCube fragment shader!"); 4417 } 4418 4419 // BlitFromCubeArray fragment shader 4420 shaderModuleCreateInfo.code = BlitFromCubeArray_metallib; 4421 shaderModuleCreateInfo.code_size = BlitFromCubeArray_metallib_len; 4422 shaderModuleCreateInfo.entrypoint = "BlitFromCubeArray"; 4423 4424 renderer->blitFromCubeArrayShader = METAL_CreateShader( 4425 (SDL_GPURenderer *)renderer, 4426 &shaderModuleCreateInfo); 4427 4428 if (renderer->blitFromCubeArrayShader == NULL) { 4429 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to compile BlitFromCubeArray fragment shader!"); 4430 } 4431 4432 // Create samplers 4433 createinfo.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; 4434 createinfo.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; 4435 createinfo.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; 4436 createinfo.enable_anisotropy = 0; 4437 createinfo.enable_compare = 0; 4438 createinfo.mag_filter = SDL_GPU_FILTER_NEAREST; 4439 createinfo.min_filter = SDL_GPU_FILTER_NEAREST; 4440 createinfo.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST; 4441 createinfo.mip_lod_bias = 0.0f; 4442 createinfo.min_lod = 0; 4443 createinfo.max_lod = 1000; 4444 createinfo.max_anisotropy = 1.0f; 4445 createinfo.compare_op = SDL_GPU_COMPAREOP_ALWAYS; 4446 4447 renderer->blitNearestSampler = METAL_CreateSampler( 4448 (SDL_GPURenderer *)renderer, 4449 &createinfo); 4450 4451 if (renderer->blitNearestSampler == NULL) { 4452 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create blit nearest sampler!"); 4453 } 4454 4455 createinfo.mag_filter = SDL_GPU_FILTER_LINEAR; 4456 createinfo.min_filter = SDL_GPU_FILTER_LINEAR; 4457 createinfo.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR; 4458 4459 renderer->blitLinearSampler = METAL_CreateSampler( 4460 (SDL_GPURenderer *)renderer, 4461 &createinfo); 4462 4463 if (renderer->blitLinearSampler == NULL) { 4464 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create blit linear sampler!"); 4465 } 4466} 4467 4468static void METAL_INTERNAL_DestroyBlitResources( 4469 SDL_GPURenderer *driverData) 4470{ 4471 MetalRenderer *renderer = (MetalRenderer *)driverData; 4472 METAL_ReleaseSampler(driverData, renderer->blitLinearSampler); 4473 METAL_ReleaseSampler(driverData, renderer->blitNearestSampler); 4474 METAL_ReleaseShader(driverData, renderer->blitVertexShader); 4475 METAL_ReleaseShader(driverData, renderer->blitFrom2DShader); 4476 METAL_ReleaseShader(driverData, renderer->blitFrom2DArrayShader); 4477 METAL_ReleaseShader(driverData, renderer->blitFrom3DShader); 4478 METAL_ReleaseShader(driverData, renderer->blitFromCubeShader); 4479 METAL_ReleaseShader(driverData, renderer->blitFromCubeArrayShader); 4480 4481 for (Uint32 i = 0; i < renderer->blitPipelineCount; i += 1) { 4482 METAL_ReleaseGraphicsPipeline(driverData, renderer->blitPipelines[i].pipeline); 4483 } 4484 SDL_free(renderer->blitPipelines); 4485} 4486 4487static SDL_GPUDevice *METAL_CreateDevice(bool debugMode, bool preferLowPower, SDL_PropertiesID props) 4488{ 4489 @autoreleasepool { 4490 MetalRenderer *renderer; 4491 id<MTLDevice> device = NULL; 4492 bool hasHardwareSupport = false; 4493 4494 bool verboseLogs = SDL_GetBooleanProperty( 4495 props, 4496 SDL_PROP_GPU_DEVICE_CREATE_VERBOSE_BOOLEAN, 4497 true); 4498 4499 if (debugMode) { 4500 /* Due to a Metal driver quirk, once a MTLDevice has been created 4501 * with this environment variable set, the Metal validation layers 4502 * will remain enabled for the rest of the application's lifespan, 4503 * even if the device is destroyed and recreated. 4504 */ 4505 SDL_setenv_unsafe("MTL_DEBUG_LAYER", "1", 0); 4506 } 4507 4508 // Create the Metal device and command queue 4509#ifdef SDL_PLATFORM_MACOS 4510 if (preferLowPower) { 4511 NSArray<id<MTLDevice>> *devices = MTLCopyAllDevices(); 4512 for (id<MTLDevice> candidate in devices) { 4513 if (candidate.isLowPower) { 4514 device = candidate; 4515 break; 4516 } 4517 } 4518 } 4519#endif 4520 if (device == NULL) { 4521 device = MTLCreateSystemDefaultDevice(); 4522 if (device == NULL) { 4523 SDL_SetError("Failed to create Metal device"); 4524 return NULL; 4525 } 4526 } 4527 4528#ifdef SDL_PLATFORM_MACOS 4529 hasHardwareSupport = true; 4530 if (@available(macOS 10.15, *)) { 4531 hasHardwareSupport = [device supportsFamily:MTLGPUFamilyMac2]; 4532 } else if (@available(macOS 10.14, *)) { 4533 hasHardwareSupport = [device supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily2_v1]; 4534 } 4535#elif defined(SDL_PLATFORM_VISIONOS) 4536 hasHardwareSupport = true; 4537#else 4538 if (@available(iOS 13.0, tvOS 13.0, *)) { 4539 hasHardwareSupport = [device supportsFamily:MTLGPUFamilyApple3]; 4540 } 4541#endif 4542 4543 if (!hasHardwareSupport) { 4544 SDL_SetError("Device does not meet the hardware requirements for SDL_GPU Metal"); 4545 return NULL; 4546 } 4547 4548 // Allocate and zero out the renderer 4549 renderer = (MetalRenderer *)SDL_calloc(1, sizeof(MetalRenderer)); 4550 4551 renderer->device = device; 4552 renderer->queue = [device newCommandQueue]; 4553 4554 renderer->props = SDL_CreateProperties(); 4555 if (verboseLogs) { 4556 SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "SDL_GPU Driver: Metal"); 4557 } 4558 4559 // Record device name 4560 const char *deviceName = [device.name UTF8String]; 4561 SDL_SetStringProperty( 4562 renderer->props, 4563 SDL_PROP_GPU_DEVICE_NAME_STRING, 4564 deviceName); 4565 if (verboseLogs) { 4566 SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Metal Device: %s", deviceName); 4567 } 4568 4569 // Remember debug mode 4570 renderer->debugMode = debugMode; 4571 renderer->allowedFramesInFlight = 2; 4572 4573 // Set up colorspace array 4574 SwapchainCompositionToColorSpace[0] = kCGColorSpaceSRGB; 4575 SwapchainCompositionToColorSpace[1] = kCGColorSpaceSRGB; 4576 SwapchainCompositionToColorSpace[2] = kCGColorSpaceExtendedLinearSRGB; 4577 if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) { 4578 SwapchainCompositionToColorSpace[3] = kCGColorSpaceITUR_2100_PQ; 4579 } else { 4580 SwapchainCompositionToColorSpace[3] = NULL; 4581 } 4582 4583 // Create mutexes 4584 renderer->submitLock = SDL_CreateMutex(); 4585 renderer->acquireCommandBufferLock = SDL_CreateMutex(); 4586 renderer->acquireUniformBufferLock = SDL_CreateMutex(); 4587 renderer->disposeLock = SDL_CreateMutex(); 4588 renderer->fenceLock = SDL_CreateMutex(); 4589 renderer->windowLock = SDL_CreateMutex(); 4590 4591 // Create command buffer pool 4592 METAL_INTERNAL_AllocateCommandBuffers(renderer, 2); 4593 4594 // Create fence pool 4595 renderer->availableFenceCapacity = 2; 4596 renderer->availableFences = SDL_calloc( 4597 renderer->availableFenceCapacity, sizeof(MetalFence *)); 4598 4599 // Create uniform buffer pool 4600 renderer->uniformBufferPoolCapacity = 32; 4601 renderer->uniformBufferPoolCount = 32; 4602 renderer->uniformBufferPool = SDL_calloc( 4603 renderer->uniformBufferPoolCapacity, sizeof(MetalUniformBuffer *)); 4604 4605 for (Uint32 i = 0; i < renderer->uniformBufferPoolCount; i += 1) { 4606 renderer->uniformBufferPool[i] = METAL_INTERNAL_CreateUniformBuffer( 4607 renderer, 4608 UNIFORM_BUFFER_SIZE); 4609 } 4610 4611 // Create deferred destroy arrays 4612 renderer->bufferContainersToDestroyCapacity = 2; 4613 renderer->bufferContainersToDestroyCount = 0; 4614 renderer->bufferContainersToDestroy = SDL_calloc( 4615 renderer->bufferContainersToDestroyCapacity, sizeof(MetalBufferContainer *)); 4616 4617 renderer->textureContainersToDestroyCapacity = 2; 4618 renderer->textureContainersToDestroyCount = 0; 4619 renderer->textureContainersToDestroy = SDL_calloc( 4620 renderer->textureContainersToDestroyCapacity, sizeof(MetalTextureContainer *)); 4621 4622 // Create claimed window list 4623 renderer->claimedWindowCapacity = 1; 4624 renderer->claimedWindows = SDL_calloc( 4625 renderer->claimedWindowCapacity, sizeof(MetalWindowData *)); 4626 4627 // Initialize blit resources 4628 METAL_INTERNAL_InitBlitResources(renderer); 4629 4630 SDL_GPUDevice *result = SDL_calloc(1, sizeof(SDL_GPUDevice)); 4631 ASSIGN_DRIVER(METAL) 4632 result->driverData = (SDL_GPURenderer *)renderer; 4633 result->shader_formats = SDL_GPU_SHADERFORMAT_MSL | SDL_GPU_SHADERFORMAT_METALLIB; 4634 renderer->sdlGPUDevice = result; 4635 4636 return result; 4637 } 4638} 4639 4640SDL_GPUBootstrap MetalDriver = { 4641 "metal", 4642 METAL_PrepareDriver, 4643 METAL_CreateDevice 4644}; 4645 4646#endif // SDL_GPU_METAL 4647[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.