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