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