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_VULKAN
25
26// Needed for VK_KHR_portability_subset
27#define VK_ENABLE_BETA_EXTENSIONS
28
29#define VK_NO_PROTOTYPES
30#include "../../video/khronos/vulkan/vulkan.h"
31
32#include <SDL3/SDL_vulkan.h>
33
34#include "../SDL_sysgpu.h"
35
36// Global Vulkan Loader Entry Points
37
38static PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
39
40#define VULKAN_GLOBAL_FUNCTION(name) \
41 static PFN_##name name = NULL;
42#include "SDL_gpu_vulkan_vkfuncs.h"
43
44typedef struct VulkanExtensions
45{
46 // These extensions are required!
47
48 // Globally supported
49 Uint8 KHR_swapchain;
50 // Core since 1.1, needed for negative VkViewport::height
51 Uint8 KHR_maintenance1;
52
53 // These extensions are optional!
54
55 // Core since 1.2, but requires annoying paperwork to implement
56 Uint8 KHR_driver_properties;
57 // Only required for special implementations (i.e. MoltenVK)
58 Uint8 KHR_portability_subset;
59 // Only required for decoding HDR ASTC textures
60 Uint8 EXT_texture_compression_astc_hdr;
61} VulkanExtensions;
62
63// Defines
64
65#define SMALL_ALLOCATION_THRESHOLD 2097152 // 2 MiB
66#define SMALL_ALLOCATION_SIZE 16777216 // 16 MiB
67#define LARGE_ALLOCATION_INCREMENT 67108864 // 64 MiB
68#define MAX_UBO_SECTION_SIZE 4096 // 4 KiB
69#define DESCRIPTOR_POOL_SIZE 128
70#define WINDOW_PROPERTY_DATA "SDL_GPUVulkanWindowPropertyData"
71
72#define IDENTITY_SWIZZLE \
73 { \
74 VK_COMPONENT_SWIZZLE_IDENTITY, \
75 VK_COMPONENT_SWIZZLE_IDENTITY, \
76 VK_COMPONENT_SWIZZLE_IDENTITY, \
77 VK_COMPONENT_SWIZZLE_IDENTITY \
78 }
79
80// Conversions
81
82static const Uint8 DEVICE_PRIORITY_HIGHPERFORMANCE[] = {
83 0, // VK_PHYSICAL_DEVICE_TYPE_OTHER
84 3, // VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU
85 4, // VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
86 2, // VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU
87 1 // VK_PHYSICAL_DEVICE_TYPE_CPU
88};
89
90static const Uint8 DEVICE_PRIORITY_LOWPOWER[] = {
91 0, // VK_PHYSICAL_DEVICE_TYPE_OTHER
92 4, // VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU
93 3, // VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
94 2, // VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU
95 1 // VK_PHYSICAL_DEVICE_TYPE_CPU
96};
97
98static VkPresentModeKHR SDLToVK_PresentMode[] = {
99 VK_PRESENT_MODE_FIFO_KHR,
100 VK_PRESENT_MODE_IMMEDIATE_KHR,
101 VK_PRESENT_MODE_MAILBOX_KHR
102};
103
104static VkFormat SDLToVK_TextureFormat[] = {
105 VK_FORMAT_UNDEFINED, // INVALID
106 VK_FORMAT_R8_UNORM, // A8_UNORM
107 VK_FORMAT_R8_UNORM, // R8_UNORM
108 VK_FORMAT_R8G8_UNORM, // R8G8_UNORM
109 VK_FORMAT_R8G8B8A8_UNORM, // R8G8B8A8_UNORM
110 VK_FORMAT_R16_UNORM, // R16_UNORM
111 VK_FORMAT_R16G16_UNORM, // R16G16_UNORM
112 VK_FORMAT_R16G16B16A16_UNORM, // R16G16B16A16_UNORM
113 VK_FORMAT_A2B10G10R10_UNORM_PACK32, // R10G10B10A2_UNORM
114 VK_FORMAT_R5G6B5_UNORM_PACK16, // B5G6R5_UNORM
115 VK_FORMAT_A1R5G5B5_UNORM_PACK16, // B5G5R5A1_UNORM
116 VK_FORMAT_B4G4R4A4_UNORM_PACK16, // B4G4R4A4_UNORM
117 VK_FORMAT_B8G8R8A8_UNORM, // B8G8R8A8_UNORM
118 VK_FORMAT_BC1_RGBA_UNORM_BLOCK, // BC1_UNORM
119 VK_FORMAT_BC2_UNORM_BLOCK, // BC2_UNORM
120 VK_FORMAT_BC3_UNORM_BLOCK, // BC3_UNORM
121 VK_FORMAT_BC4_UNORM_BLOCK, // BC4_UNORM
122 VK_FORMAT_BC5_UNORM_BLOCK, // BC5_UNORM
123 VK_FORMAT_BC7_UNORM_BLOCK, // BC7_UNORM
124 VK_FORMAT_BC6H_SFLOAT_BLOCK, // BC6H_FLOAT
125 VK_FORMAT_BC6H_UFLOAT_BLOCK, // BC6H_UFLOAT
126 VK_FORMAT_R8_SNORM, // R8_SNORM
127 VK_FORMAT_R8G8_SNORM, // R8G8_SNORM
128 VK_FORMAT_R8G8B8A8_SNORM, // R8G8B8A8_SNORM
129 VK_FORMAT_R16_SNORM, // R16_SNORM
130 VK_FORMAT_R16G16_SNORM, // R16G16_SNORM
131 VK_FORMAT_R16G16B16A16_SNORM, // R16G16B16A16_SNORM
132 VK_FORMAT_R16_SFLOAT, // R16_FLOAT
133 VK_FORMAT_R16G16_SFLOAT, // R16G16_FLOAT
134 VK_FORMAT_R16G16B16A16_SFLOAT, // R16G16B16A16_FLOAT
135 VK_FORMAT_R32_SFLOAT, // R32_FLOAT
136 VK_FORMAT_R32G32_SFLOAT, // R32G32_FLOAT
137 VK_FORMAT_R32G32B32A32_SFLOAT, // R32G32B32A32_FLOAT
138 VK_FORMAT_B10G11R11_UFLOAT_PACK32, // R11G11B10_UFLOAT
139 VK_FORMAT_R8_UINT, // R8_UINT
140 VK_FORMAT_R8G8_UINT, // R8G8_UINT
141 VK_FORMAT_R8G8B8A8_UINT, // R8G8B8A8_UINT
142 VK_FORMAT_R16_UINT, // R16_UINT
143 VK_FORMAT_R16G16_UINT, // R16G16_UINT
144 VK_FORMAT_R16G16B16A16_UINT, // R16G16B16A16_UINT
145 VK_FORMAT_R32_UINT, // R32_UINT
146 VK_FORMAT_R32G32_UINT, // R32G32_UINT
147 VK_FORMAT_R32G32B32A32_UINT, // R32G32B32A32_UINT
148 VK_FORMAT_R8_SINT, // R8_INT
149 VK_FORMAT_R8G8_SINT, // R8G8_INT
150 VK_FORMAT_R8G8B8A8_SINT, // R8G8B8A8_INT
151 VK_FORMAT_R16_SINT, // R16_INT
152 VK_FORMAT_R16G16_SINT, // R16G16_INT
153 VK_FORMAT_R16G16B16A16_SINT, // R16G16B16A16_INT
154 VK_FORMAT_R32_SINT, // R32_INT
155 VK_FORMAT_R32G32_SINT, // R32G32_INT
156 VK_FORMAT_R32G32B32A32_SINT, // R32G32B32A32_INT
157 VK_FORMAT_R8G8B8A8_SRGB, // R8G8B8A8_UNORM_SRGB
158 VK_FORMAT_B8G8R8A8_SRGB, // B8G8R8A8_UNORM_SRGB
159 VK_FORMAT_BC1_RGBA_SRGB_BLOCK, // BC1_UNORM_SRGB
160 VK_FORMAT_BC2_SRGB_BLOCK, // BC3_UNORM_SRGB
161 VK_FORMAT_BC3_SRGB_BLOCK, // BC3_UNORM_SRGB
162 VK_FORMAT_BC7_SRGB_BLOCK, // BC7_UNORM_SRGB
163 VK_FORMAT_D16_UNORM, // D16_UNORM
164 VK_FORMAT_X8_D24_UNORM_PACK32, // D24_UNORM
165 VK_FORMAT_D32_SFLOAT, // D32_FLOAT
166 VK_FORMAT_D24_UNORM_S8_UINT, // D24_UNORM_S8_UINT
167 VK_FORMAT_D32_SFLOAT_S8_UINT, // D32_FLOAT_S8_UINT
168 VK_FORMAT_ASTC_4x4_UNORM_BLOCK, // ASTC_4x4_UNORM
169 VK_FORMAT_ASTC_5x4_UNORM_BLOCK, // ASTC_5x4_UNORM
170 VK_FORMAT_ASTC_5x5_UNORM_BLOCK, // ASTC_5x5_UNORM
171 VK_FORMAT_ASTC_6x5_UNORM_BLOCK, // ASTC_6x5_UNORM
172 VK_FORMAT_ASTC_6x6_UNORM_BLOCK, // ASTC_6x6_UNORM
173 VK_FORMAT_ASTC_8x5_UNORM_BLOCK, // ASTC_8x5_UNORM
174 VK_FORMAT_ASTC_8x6_UNORM_BLOCK, // ASTC_8x6_UNORM
175 VK_FORMAT_ASTC_8x8_UNORM_BLOCK, // ASTC_8x8_UNORM
176 VK_FORMAT_ASTC_10x5_UNORM_BLOCK, // ASTC_10x5_UNORM
177 VK_FORMAT_ASTC_10x6_UNORM_BLOCK, // ASTC_10x6_UNORM
178 VK_FORMAT_ASTC_10x8_UNORM_BLOCK, // ASTC_10x8_UNORM
179 VK_FORMAT_ASTC_10x10_UNORM_BLOCK, // ASTC_10x10_UNORM
180 VK_FORMAT_ASTC_12x10_UNORM_BLOCK, // ASTC_12x10_UNORM
181 VK_FORMAT_ASTC_12x12_UNORM_BLOCK, // ASTC_12x12_UNORM
182 VK_FORMAT_ASTC_4x4_SRGB_BLOCK, // ASTC_4x4_UNORM_SRGB
183 VK_FORMAT_ASTC_5x4_SRGB_BLOCK, // ASTC_5x4_UNORM_SRGB
184 VK_FORMAT_ASTC_5x5_SRGB_BLOCK, // ASTC_5x5_UNORM_SRGB
185 VK_FORMAT_ASTC_6x5_SRGB_BLOCK, // ASTC_6x5_UNORM_SRGB
186 VK_FORMAT_ASTC_6x6_SRGB_BLOCK, // ASTC_6x6_UNORM_SRGB
187 VK_FORMAT_ASTC_8x5_SRGB_BLOCK, // ASTC_8x5_UNORM_SRGB
188 VK_FORMAT_ASTC_8x6_SRGB_BLOCK, // ASTC_8x6_UNORM_SRGB
189 VK_FORMAT_ASTC_8x8_SRGB_BLOCK, // ASTC_8x8_UNORM_SRGB
190 VK_FORMAT_ASTC_10x5_SRGB_BLOCK, // ASTC_10x5_UNORM_SRGB
191 VK_FORMAT_ASTC_10x6_SRGB_BLOCK, // ASTC_10x6_UNORM_SRGB
192 VK_FORMAT_ASTC_10x8_SRGB_BLOCK, // ASTC_10x8_UNORM_SRGB
193 VK_FORMAT_ASTC_10x10_SRGB_BLOCK, // ASTC_10x10_UNORM_SRGB
194 VK_FORMAT_ASTC_12x10_SRGB_BLOCK, // ASTC_12x10_UNORM_SRGB
195 VK_FORMAT_ASTC_12x12_SRGB_BLOCK, // ASTC_12x12_UNORM_SRGB
196 VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT, // ASTC_4x4_FLOAT
197 VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT, // ASTC_5x4_FLOAT
198 VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT, // ASTC_5x5_FLOAT
199 VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT, // ASTC_6x5_FLOAT
200 VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT, // ASTC_6x6_FLOAT
201 VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT, // ASTC_8x5_FLOAT
202 VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT, // ASTC_8x6_FLOAT
203 VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT, // ASTC_8x8_FLOAT
204 VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT, // ASTC_10x5_FLOAT
205 VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT, // ASTC_10x6_FLOAT
206 VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT, // ASTC_10x8_FLOAT
207 VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT, // ASTC_10x10_FLOAT
208 VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT, // ASTC_12x10_FLOAT
209 VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK // ASTC_12x12_FLOAT
210};
211SDL_COMPILE_TIME_ASSERT(SDLToVK_TextureFormat, SDL_arraysize(SDLToVK_TextureFormat) == SDL_GPU_TEXTUREFORMAT_MAX_ENUM_VALUE);
212
213static VkComponentMapping SwizzleForSDLFormat(SDL_GPUTextureFormat format)
214{
215 if (format == SDL_GPU_TEXTUREFORMAT_A8_UNORM) {
216 // TODO: use VK_FORMAT_A8_UNORM_KHR from VK_KHR_maintenance5 when available
217 return (VkComponentMapping){
218 VK_COMPONENT_SWIZZLE_ZERO,
219 VK_COMPONENT_SWIZZLE_ZERO,
220 VK_COMPONENT_SWIZZLE_ZERO,
221 VK_COMPONENT_SWIZZLE_R,
222 };
223 }
224
225 if (format == SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM) {
226 // ARGB -> BGRA
227 // TODO: use VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT from VK_EXT_4444_formats when available
228 return (VkComponentMapping){
229 VK_COMPONENT_SWIZZLE_G,
230 VK_COMPONENT_SWIZZLE_R,
231 VK_COMPONENT_SWIZZLE_A,
232 VK_COMPONENT_SWIZZLE_B,
233 };
234 }
235
236 return (VkComponentMapping)IDENTITY_SWIZZLE;
237}
238
239static VkFormat SwapchainCompositionToFormat[] = {
240 VK_FORMAT_B8G8R8A8_UNORM, // SDR
241 VK_FORMAT_B8G8R8A8_SRGB, // SDR_LINEAR
242 VK_FORMAT_R16G16B16A16_SFLOAT, // HDR_EXTENDED_LINEAR
243 VK_FORMAT_A2B10G10R10_UNORM_PACK32 // HDR10_ST2084
244};
245
246static VkFormat SwapchainCompositionToFallbackFormat[] = {
247 VK_FORMAT_R8G8B8A8_UNORM, // SDR
248 VK_FORMAT_R8G8B8A8_SRGB, // SDR_LINEAR
249 VK_FORMAT_UNDEFINED, // HDR_EXTENDED_LINEAR (no fallback)
250 VK_FORMAT_UNDEFINED // HDR10_ST2084 (no fallback)
251};
252
253static SDL_GPUTextureFormat SwapchainCompositionToSDLFormat(
254 SDL_GPUSwapchainComposition composition,
255 bool usingFallback)
256{
257 switch (composition) {
258 case SDL_GPU_SWAPCHAINCOMPOSITION_SDR:
259 return usingFallback ? SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM : SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM;
260 case SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR:
261 return usingFallback ? SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB : SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB;
262 case SDL_GPU_SWAPCHAINCOMPOSITION_HDR_EXTENDED_LINEAR:
263 return SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT;
264 case SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2084:
265 return SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM;
266 default:
267 return SDL_GPU_TEXTUREFORMAT_INVALID;
268 }
269}
270
271static VkColorSpaceKHR SwapchainCompositionToColorSpace[] = {
272 VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, // SDR
273 VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, // SDR_LINEAR
274 VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT, // HDR_EXTENDED_LINEAR
275 VK_COLOR_SPACE_HDR10_ST2084_EXT // HDR10_ST2084
276};
277
278static VkComponentMapping SwapchainCompositionSwizzle[] = {
279 IDENTITY_SWIZZLE, // SDR
280 IDENTITY_SWIZZLE, // SDR_LINEAR
281 IDENTITY_SWIZZLE, // HDR_EXTENDED_LINEAR
282 {
283 // HDR10_ST2084
284 VK_COMPONENT_SWIZZLE_R,
285 VK_COMPONENT_SWIZZLE_G,
286 VK_COMPONENT_SWIZZLE_B,
287 VK_COMPONENT_SWIZZLE_A,
288 }
289};
290
291static VkFormat SDLToVK_VertexFormat[] = {
292 VK_FORMAT_UNDEFINED, // INVALID
293 VK_FORMAT_R32_SINT, // INT
294 VK_FORMAT_R32G32_SINT, // INT2
295 VK_FORMAT_R32G32B32_SINT, // INT3
296 VK_FORMAT_R32G32B32A32_SINT, // INT4
297 VK_FORMAT_R32_UINT, // UINT
298 VK_FORMAT_R32G32_UINT, // UINT2
299 VK_FORMAT_R32G32B32_UINT, // UINT3
300 VK_FORMAT_R32G32B32A32_UINT, // UINT4
301 VK_FORMAT_R32_SFLOAT, // FLOAT
302 VK_FORMAT_R32G32_SFLOAT, // FLOAT2
303 VK_FORMAT_R32G32B32_SFLOAT, // FLOAT3
304 VK_FORMAT_R32G32B32A32_SFLOAT, // FLOAT4
305 VK_FORMAT_R8G8_SINT, // BYTE2
306 VK_FORMAT_R8G8B8A8_SINT, // BYTE4
307 VK_FORMAT_R8G8_UINT, // UBYTE2
308 VK_FORMAT_R8G8B8A8_UINT, // UBYTE4
309 VK_FORMAT_R8G8_SNORM, // BYTE2_NORM
310 VK_FORMAT_R8G8B8A8_SNORM, // BYTE4_NORM
311 VK_FORMAT_R8G8_UNORM, // UBYTE2_NORM
312 VK_FORMAT_R8G8B8A8_UNORM, // UBYTE4_NORM
313 VK_FORMAT_R16G16_SINT, // SHORT2
314 VK_FORMAT_R16G16B16A16_SINT, // SHORT4
315 VK_FORMAT_R16G16_UINT, // USHORT2
316 VK_FORMAT_R16G16B16A16_UINT, // USHORT4
317 VK_FORMAT_R16G16_SNORM, // SHORT2_NORM
318 VK_FORMAT_R16G16B16A16_SNORM, // SHORT4_NORM
319 VK_FORMAT_R16G16_UNORM, // USHORT2_NORM
320 VK_FORMAT_R16G16B16A16_UNORM, // USHORT4_NORM
321 VK_FORMAT_R16G16_SFLOAT, // HALF2
322 VK_FORMAT_R16G16B16A16_SFLOAT // HALF4
323};
324SDL_COMPILE_TIME_ASSERT(SDLToVK_VertexFormat, SDL_arraysize(SDLToVK_VertexFormat) == SDL_GPU_VERTEXELEMENTFORMAT_MAX_ENUM_VALUE);
325
326static VkIndexType SDLToVK_IndexType[] = {
327 VK_INDEX_TYPE_UINT16,
328 VK_INDEX_TYPE_UINT32
329};
330
331static VkPrimitiveTopology SDLToVK_PrimitiveType[] = {
332 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
333 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
334 VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
335 VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,
336 VK_PRIMITIVE_TOPOLOGY_POINT_LIST
337};
338
339static VkCullModeFlags SDLToVK_CullMode[] = {
340 VK_CULL_MODE_NONE,
341 VK_CULL_MODE_FRONT_BIT,
342 VK_CULL_MODE_BACK_BIT,
343 VK_CULL_MODE_FRONT_AND_BACK
344};
345
346static VkFrontFace SDLToVK_FrontFace[] = {
347 VK_FRONT_FACE_COUNTER_CLOCKWISE,
348 VK_FRONT_FACE_CLOCKWISE
349};
350
351static VkBlendFactor SDLToVK_BlendFactor[] = {
352 VK_BLEND_FACTOR_ZERO, // INVALID
353 VK_BLEND_FACTOR_ZERO,
354 VK_BLEND_FACTOR_ONE,
355 VK_BLEND_FACTOR_SRC_COLOR,
356 VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR,
357 VK_BLEND_FACTOR_DST_COLOR,
358 VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR,
359 VK_BLEND_FACTOR_SRC_ALPHA,
360 VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
361 VK_BLEND_FACTOR_DST_ALPHA,
362 VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,
363 VK_BLEND_FACTOR_CONSTANT_COLOR,
364 VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
365 VK_BLEND_FACTOR_SRC_ALPHA_SATURATE
366};
367SDL_COMPILE_TIME_ASSERT(SDLToVK_BlendFactor, SDL_arraysize(SDLToVK_BlendFactor) == SDL_GPU_BLENDFACTOR_MAX_ENUM_VALUE);
368
369static VkBlendOp SDLToVK_BlendOp[] = {
370 VK_BLEND_OP_ADD, // INVALID
371 VK_BLEND_OP_ADD,
372 VK_BLEND_OP_SUBTRACT,
373 VK_BLEND_OP_REVERSE_SUBTRACT,
374 VK_BLEND_OP_MIN,
375 VK_BLEND_OP_MAX
376};
377SDL_COMPILE_TIME_ASSERT(SDLToVK_BlendOp, SDL_arraysize(SDLToVK_BlendOp) == SDL_GPU_BLENDOP_MAX_ENUM_VALUE);
378
379static VkCompareOp SDLToVK_CompareOp[] = {
380 VK_COMPARE_OP_NEVER, // INVALID
381 VK_COMPARE_OP_NEVER,
382 VK_COMPARE_OP_LESS,
383 VK_COMPARE_OP_EQUAL,
384 VK_COMPARE_OP_LESS_OR_EQUAL,
385 VK_COMPARE_OP_GREATER,
386 VK_COMPARE_OP_NOT_EQUAL,
387 VK_COMPARE_OP_GREATER_OR_EQUAL,
388 VK_COMPARE_OP_ALWAYS
389};
390SDL_COMPILE_TIME_ASSERT(SDLToVK_CompareOp, SDL_arraysize(SDLToVK_CompareOp) == SDL_GPU_COMPAREOP_MAX_ENUM_VALUE);
391
392static VkStencilOp SDLToVK_StencilOp[] = {
393 VK_STENCIL_OP_KEEP, // INVALID
394 VK_STENCIL_OP_KEEP,
395 VK_STENCIL_OP_ZERO,
396 VK_STENCIL_OP_REPLACE,
397 VK_STENCIL_OP_INCREMENT_AND_CLAMP,
398 VK_STENCIL_OP_DECREMENT_AND_CLAMP,
399 VK_STENCIL_OP_INVERT,
400 VK_STENCIL_OP_INCREMENT_AND_WRAP,
401 VK_STENCIL_OP_DECREMENT_AND_WRAP
402};
403SDL_COMPILE_TIME_ASSERT(SDLToVK_StencilOp, SDL_arraysize(SDLToVK_StencilOp) == SDL_GPU_STENCILOP_MAX_ENUM_VALUE);
404
405static VkAttachmentLoadOp SDLToVK_LoadOp[] = {
406 VK_ATTACHMENT_LOAD_OP_LOAD,
407 VK_ATTACHMENT_LOAD_OP_CLEAR,
408 VK_ATTACHMENT_LOAD_OP_DONT_CARE
409};
410
411static VkAttachmentStoreOp SDLToVK_StoreOp[] = {
412 VK_ATTACHMENT_STORE_OP_STORE,
413 VK_ATTACHMENT_STORE_OP_DONT_CARE,
414 VK_ATTACHMENT_STORE_OP_DONT_CARE,
415 VK_ATTACHMENT_STORE_OP_STORE
416};
417
418static VkSampleCountFlagBits SDLToVK_SampleCount[] = {
419 VK_SAMPLE_COUNT_1_BIT,
420 VK_SAMPLE_COUNT_2_BIT,
421 VK_SAMPLE_COUNT_4_BIT,
422 VK_SAMPLE_COUNT_8_BIT
423};
424
425static VkVertexInputRate SDLToVK_VertexInputRate[] = {
426 VK_VERTEX_INPUT_RATE_VERTEX,
427 VK_VERTEX_INPUT_RATE_INSTANCE
428};
429
430static VkFilter SDLToVK_Filter[] = {
431 VK_FILTER_NEAREST,
432 VK_FILTER_LINEAR
433};
434
435static VkSamplerMipmapMode SDLToVK_SamplerMipmapMode[] = {
436 VK_SAMPLER_MIPMAP_MODE_NEAREST,
437 VK_SAMPLER_MIPMAP_MODE_LINEAR
438};
439
440static VkSamplerAddressMode SDLToVK_SamplerAddressMode[] = {
441 VK_SAMPLER_ADDRESS_MODE_REPEAT,
442 VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT,
443 VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE
444};
445
446// Structures
447
448typedef struct VulkanMemoryAllocation VulkanMemoryAllocation;
449typedef struct VulkanBuffer VulkanBuffer;
450typedef struct VulkanBufferContainer VulkanBufferContainer;
451typedef struct VulkanUniformBuffer VulkanUniformBuffer;
452typedef struct VulkanTexture VulkanTexture;
453typedef struct VulkanTextureContainer VulkanTextureContainer;
454
455typedef struct VulkanFenceHandle
456{
457 VkFence fence;
458 SDL_AtomicInt referenceCount;
459} VulkanFenceHandle;
460
461// Memory Allocation
462
463typedef struct VulkanMemoryFreeRegion
464{
465 VulkanMemoryAllocation *allocation;
466 VkDeviceSize offset;
467 VkDeviceSize size;
468 Uint32 allocationIndex;
469 Uint32 sortedIndex;
470} VulkanMemoryFreeRegion;
471
472typedef struct VulkanMemoryUsedRegion
473{
474 VulkanMemoryAllocation *allocation;
475 VkDeviceSize offset;
476 VkDeviceSize size;
477 VkDeviceSize resourceOffset; // differs from offset based on alignment
478 VkDeviceSize resourceSize; // differs from size based on alignment
479 VkDeviceSize alignment;
480 Uint8 isBuffer;
481 union
482 {
483 VulkanBuffer *vulkanBuffer;
484 VulkanTexture *vulkanTexture;
485 };
486} VulkanMemoryUsedRegion;
487
488typedef struct VulkanMemorySubAllocator
489{
490 Uint32 memoryTypeIndex;
491 VulkanMemoryAllocation **allocations;
492 Uint32 allocationCount;
493 VulkanMemoryFreeRegion **sortedFreeRegions;
494 Uint32 sortedFreeRegionCount;
495 Uint32 sortedFreeRegionCapacity;
496} VulkanMemorySubAllocator;
497
498struct VulkanMemoryAllocation
499{
500 VulkanMemorySubAllocator *allocator;
501 VkDeviceMemory memory;
502 VkDeviceSize size;
503 VulkanMemoryUsedRegion **usedRegions;
504 Uint32 usedRegionCount;
505 Uint32 usedRegionCapacity;
506 VulkanMemoryFreeRegion **freeRegions;
507 Uint32 freeRegionCount;
508 Uint32 freeRegionCapacity;
509 Uint8 availableForAllocation;
510 VkDeviceSize freeSpace;
511 VkDeviceSize usedSpace;
512 Uint8 *mapPointer;
513 SDL_Mutex *memoryLock;
514};
515
516typedef struct VulkanMemoryAllocator
517{
518 VulkanMemorySubAllocator subAllocators[VK_MAX_MEMORY_TYPES];
519} VulkanMemoryAllocator;
520
521// Memory structures
522
523typedef enum VulkanBufferType
524{
525 VULKAN_BUFFER_TYPE_GPU,
526 VULKAN_BUFFER_TYPE_UNIFORM,
527 VULKAN_BUFFER_TYPE_TRANSFER
528} VulkanBufferType;
529
530struct VulkanBuffer
531{
532 VulkanBufferContainer *container;
533 Uint32 containerIndex;
534
535 VkBuffer buffer;
536 VulkanMemoryUsedRegion *usedRegion;
537
538 // Needed for uniforms and defrag
539 VulkanBufferType type;
540 SDL_GPUBufferUsageFlags usage;
541 VkDeviceSize size;
542
543 SDL_AtomicInt referenceCount;
544 bool transitioned;
545 bool markedForDestroy; // so that defrag doesn't double-free
546 VulkanUniformBuffer *uniformBufferForDefrag;
547};
548
549struct VulkanBufferContainer
550{
551 VulkanBuffer *activeBuffer;
552
553 VulkanBuffer **buffers;
554 Uint32 bufferCapacity;
555 Uint32 bufferCount;
556
557 bool dedicated;
558 char *debugName;
559};
560
561// Renderer Structure
562
563typedef struct QueueFamilyIndices
564{
565 Uint32 graphicsFamily;
566 Uint32 presentFamily;
567 Uint32 computeFamily;
568 Uint32 transferFamily;
569} QueueFamilyIndices;
570
571typedef struct VulkanSampler
572{
573 VkSampler sampler;
574 SDL_AtomicInt referenceCount;
575} VulkanSampler;
576
577typedef struct VulkanShader
578{
579 VkShaderModule shaderModule;
580 char *entrypointName;
581 SDL_GPUShaderStage stage;
582 Uint32 numSamplers;
583 Uint32 numStorageTextures;
584 Uint32 numStorageBuffers;
585 Uint32 numUniformBuffers;
586 SDL_AtomicInt referenceCount;
587} VulkanShader;
588
589/* Textures are made up of individual subresources.
590 * This helps us barrier the resource efficiently.
591 */
592typedef struct VulkanTextureSubresource
593{
594 VulkanTexture *parent;
595 Uint32 layer;
596 Uint32 level;
597
598 VkImageView *renderTargetViews; // One render target view per depth slice
599 VkImageView computeWriteView;
600 VkImageView depthStencilView;
601} VulkanTextureSubresource;
602
603struct VulkanTexture
604{
605 VulkanTextureContainer *container;
606 Uint32 containerIndex;
607
608 VulkanMemoryUsedRegion *usedRegion;
609
610 VkImage image;
611 VkImageView fullView; // used for samplers and storage reads
612 VkComponentMapping swizzle;
613 VkImageAspectFlags aspectFlags;
614 Uint32 depth; // used for cleanup only
615
616 // FIXME: It'd be nice if we didn't have to have this on the texture...
617 SDL_GPUTextureUsageFlags usage; // used for defrag transitions only.
618
619 Uint32 subresourceCount;
620 VulkanTextureSubresource *subresources;
621
622 bool markedForDestroy; // so that defrag doesn't double-free
623 SDL_AtomicInt referenceCount;
624};
625
626struct VulkanTextureContainer
627{
628 TextureCommonHeader header;
629
630 VulkanTexture *activeTexture;
631
632 Uint32 textureCapacity;
633 Uint32 textureCount;
634 VulkanTexture **textures;
635
636 char *debugName;
637 bool canBeCycled;
638};
639
640typedef enum VulkanBufferUsageMode
641{
642 VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE,
643 VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION,
644 VULKAN_BUFFER_USAGE_MODE_VERTEX_READ,
645 VULKAN_BUFFER_USAGE_MODE_INDEX_READ,
646 VULKAN_BUFFER_USAGE_MODE_INDIRECT,
647 VULKAN_BUFFER_USAGE_MODE_GRAPHICS_STORAGE_READ,
648 VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ,
649 VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE,
650} VulkanBufferUsageMode;
651
652typedef enum VulkanTextureUsageMode
653{
654 VULKAN_TEXTURE_USAGE_MODE_UNINITIALIZED,
655 VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
656 VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
657 VULKAN_TEXTURE_USAGE_MODE_SAMPLER,
658 VULKAN_TEXTURE_USAGE_MODE_GRAPHICS_STORAGE_READ,
659 VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ,
660 VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE,
661 VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT,
662 VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT,
663 VULKAN_TEXTURE_USAGE_MODE_PRESENT
664} VulkanTextureUsageMode;
665
666typedef enum VulkanUniformBufferStage
667{
668 VULKAN_UNIFORM_BUFFER_STAGE_VERTEX,
669 VULKAN_UNIFORM_BUFFER_STAGE_FRAGMENT,
670 VULKAN_UNIFORM_BUFFER_STAGE_COMPUTE
671} VulkanUniformBufferStage;
672
673typedef struct VulkanFramebuffer
674{
675 VkFramebuffer framebuffer;
676 SDL_AtomicInt referenceCount;
677} VulkanFramebuffer;
678
679typedef struct WindowData
680{
681 SDL_Window *window;
682 SDL_GPUSwapchainComposition swapchainComposition;
683 SDL_GPUPresentMode presentMode;
684 bool needsSwapchainRecreate;
685 Uint32 swapchainCreateWidth;
686 Uint32 swapchainCreateHeight;
687
688 // Window surface
689 VkSurfaceKHR surface;
690
691 // Swapchain for window surface
692 VkSwapchainKHR swapchain;
693 VkFormat format;
694 VkColorSpaceKHR colorSpace;
695 VkComponentMapping swapchainSwizzle;
696 bool usingFallbackFormat;
697
698 // Swapchain images
699 VulkanTextureContainer *textureContainers; // use containers so that swapchain textures can use the same API as other textures
700 Uint32 imageCount;
701 Uint32 width;
702 Uint32 height;
703
704 // Synchronization primitives
705 VkSemaphore imageAvailableSemaphore[MAX_FRAMES_IN_FLIGHT];
706 VkSemaphore renderFinishedSemaphore[MAX_FRAMES_IN_FLIGHT];
707 SDL_GPUFence *inFlightFences[MAX_FRAMES_IN_FLIGHT];
708
709 Uint32 frameCounter;
710} WindowData;
711
712typedef struct SwapchainSupportDetails
713{
714 VkSurfaceCapabilitiesKHR capabilities;
715 VkSurfaceFormatKHR *formats;
716 Uint32 formatsLength;
717 VkPresentModeKHR *presentModes;
718 Uint32 presentModesLength;
719} SwapchainSupportDetails;
720
721typedef struct VulkanPresentData
722{
723 WindowData *windowData;
724 Uint32 swapchainImageIndex;
725} VulkanPresentData;
726
727struct VulkanUniformBuffer
728{
729 VulkanBuffer *buffer;
730 Uint32 drawOffset;
731 Uint32 writeOffset;
732};
733
734typedef struct VulkanDescriptorInfo
735{
736 VkDescriptorType descriptorType;
737 VkShaderStageFlagBits stageFlag;
738} VulkanDescriptorInfo;
739
740typedef struct DescriptorSetPool
741{
742 // It's a pool... of pools!!!
743 Uint32 poolCount;
744 VkDescriptorPool *descriptorPools;
745
746 // We'll just manage the descriptor sets ourselves instead of freeing the sets
747 VkDescriptorSet *descriptorSets;
748 Uint32 descriptorSetCount;
749 Uint32 descriptorSetIndex;
750} DescriptorSetPool;
751
752// A command buffer acquires a cache at command buffer acquisition time
753typedef struct DescriptorSetCache
754{
755 // Pools are indexed by DescriptorSetLayoutID which increases monotonically
756 // There's only a certain number of maximum layouts possible since we de-duplicate them.
757 DescriptorSetPool *pools;
758 Uint32 poolCount;
759} DescriptorSetCache;
760
761typedef struct DescriptorSetLayoutHashTableKey
762{
763 VkShaderStageFlagBits shaderStage;
764 // Category 1: read resources
765 Uint32 samplerCount;
766 Uint32 storageBufferCount;
767 Uint32 storageTextureCount;
768 // Category 2: write resources
769 Uint32 writeStorageBufferCount;
770 Uint32 writeStorageTextureCount;
771 // Category 3: uniform buffers
772 Uint32 uniformBufferCount;
773} DescriptorSetLayoutHashTableKey;
774
775typedef uint32_t DescriptorSetLayoutID;
776
777typedef struct DescriptorSetLayout
778{
779 DescriptorSetLayoutID ID;
780 VkDescriptorSetLayout descriptorSetLayout;
781
782 // Category 1: read resources
783 Uint32 samplerCount;
784 Uint32 storageBufferCount;
785 Uint32 storageTextureCount;
786 // Category 2: write resources
787 Uint32 writeStorageBufferCount;
788 Uint32 writeStorageTextureCount;
789 // Category 3: uniform buffers
790 Uint32 uniformBufferCount;
791} DescriptorSetLayout;
792
793typedef struct GraphicsPipelineResourceLayoutHashTableKey
794{
795 Uint32 vertexSamplerCount;
796 Uint32 vertexStorageBufferCount;
797 Uint32 vertexStorageTextureCount;
798 Uint32 vertexUniformBufferCount;
799
800 Uint32 fragmentSamplerCount;
801 Uint32 fragmentStorageBufferCount;
802 Uint32 fragmentStorageTextureCount;
803 Uint32 fragmentUniformBufferCount;
804} GraphicsPipelineResourceLayoutHashTableKey;
805
806typedef struct VulkanGraphicsPipelineResourceLayout
807{
808 VkPipelineLayout pipelineLayout;
809
810 /*
811 * Descriptor set layout is as follows:
812 * 0: vertex resources
813 * 1: vertex uniform buffers
814 * 2: fragment resources
815 * 3: fragment uniform buffers
816 */
817 DescriptorSetLayout *descriptorSetLayouts[4];
818
819 Uint32 vertexSamplerCount;
820 Uint32 vertexStorageBufferCount;
821 Uint32 vertexStorageTextureCount;
822 Uint32 vertexUniformBufferCount;
823
824 Uint32 fragmentSamplerCount;
825 Uint32 fragmentStorageBufferCount;
826 Uint32 fragmentStorageTextureCount;
827 Uint32 fragmentUniformBufferCount;
828} VulkanGraphicsPipelineResourceLayout;
829
830typedef struct VulkanGraphicsPipeline
831{
832 VkPipeline pipeline;
833 SDL_GPUPrimitiveType primitiveType;
834
835 VulkanGraphicsPipelineResourceLayout *resourceLayout;
836
837 VulkanShader *vertexShader;
838 VulkanShader *fragmentShader;
839
840 SDL_AtomicInt referenceCount;
841} VulkanGraphicsPipeline;
842
843typedef struct ComputePipelineResourceLayoutHashTableKey
844{
845 Uint32 samplerCount;
846 Uint32 readonlyStorageTextureCount;
847 Uint32 readonlyStorageBufferCount;
848 Uint32 readWriteStorageTextureCount;
849 Uint32 readWriteStorageBufferCount;
850 Uint32 uniformBufferCount;
851} ComputePipelineResourceLayoutHashTableKey;
852
853typedef struct VulkanComputePipelineResourceLayout
854{
855 VkPipelineLayout pipelineLayout;
856
857 /*
858 * Descriptor set layout is as follows:
859 * 0: samplers, then read-only textures, then read-only buffers
860 * 1: write-only textures, then write-only buffers
861 * 2: uniform buffers
862 */
863 DescriptorSetLayout *descriptorSetLayouts[3];
864
865 Uint32 numSamplers;
866 Uint32 numReadonlyStorageTextures;
867 Uint32 numReadonlyStorageBuffers;
868 Uint32 numReadWriteStorageTextures;
869 Uint32 numReadWriteStorageBuffers;
870 Uint32 numUniformBuffers;
871} VulkanComputePipelineResourceLayout;
872
873typedef struct VulkanComputePipeline
874{
875 VkShaderModule shaderModule;
876 VkPipeline pipeline;
877 VulkanComputePipelineResourceLayout *resourceLayout;
878 SDL_AtomicInt referenceCount;
879} VulkanComputePipeline;
880
881typedef struct RenderPassColorTargetDescription
882{
883 VkFormat format;
884 SDL_GPULoadOp loadOp;
885 SDL_GPUStoreOp storeOp;
886} RenderPassColorTargetDescription;
887
888typedef struct RenderPassDepthStencilTargetDescription
889{
890 VkFormat format;
891 SDL_GPULoadOp loadOp;
892 SDL_GPUStoreOp storeOp;
893 SDL_GPULoadOp stencilLoadOp;
894 SDL_GPUStoreOp stencilStoreOp;
895} RenderPassDepthStencilTargetDescription;
896
897typedef struct CommandPoolHashTableKey
898{
899 SDL_ThreadID threadID;
900} CommandPoolHashTableKey;
901
902typedef struct RenderPassHashTableKey
903{
904 RenderPassColorTargetDescription colorTargetDescriptions[MAX_COLOR_TARGET_BINDINGS];
905 Uint32 numColorTargets;
906 VkFormat resolveTargetFormats[MAX_COLOR_TARGET_BINDINGS];
907 Uint32 numResolveTargets;
908 RenderPassDepthStencilTargetDescription depthStencilTargetDescription;
909 VkSampleCountFlagBits sampleCount;
910} RenderPassHashTableKey;
911
912typedef struct VulkanRenderPassHashTableValue
913{
914 VkRenderPass handle;
915} VulkanRenderPassHashTableValue;
916
917typedef struct FramebufferHashTableKey
918{
919 VkImageView colorAttachmentViews[MAX_COLOR_TARGET_BINDINGS];
920 Uint32 numColorTargets;
921 VkImageView resolveAttachmentViews[MAX_COLOR_TARGET_BINDINGS];
922 Uint32 numResolveAttachments;
923 VkImageView depthStencilAttachmentView;
924 Uint32 width;
925 Uint32 height;
926} FramebufferHashTableKey;
927
928// Command structures
929
930typedef struct VulkanFencePool
931{
932 SDL_Mutex *lock;
933
934 VulkanFenceHandle **availableFences;
935 Uint32 availableFenceCount;
936 Uint32 availableFenceCapacity;
937} VulkanFencePool;
938
939typedef struct VulkanCommandPool VulkanCommandPool;
940
941typedef struct VulkanRenderer VulkanRenderer;
942
943typedef struct VulkanCommandBuffer
944{
945 CommandBufferCommonHeader common;
946 VulkanRenderer *renderer;
947
948 VkCommandBuffer commandBuffer;
949 VulkanCommandPool *commandPool;
950
951 VulkanPresentData *presentDatas;
952 Uint32 presentDataCount;
953 Uint32 presentDataCapacity;
954
955 VkSemaphore *waitSemaphores;
956 Uint32 waitSemaphoreCount;
957 Uint32 waitSemaphoreCapacity;
958
959 VkSemaphore *signalSemaphores;
960 Uint32 signalSemaphoreCount;
961 Uint32 signalSemaphoreCapacity;
962
963 VulkanComputePipeline *currentComputePipeline;
964 VulkanGraphicsPipeline *currentGraphicsPipeline;
965
966 // Keep track of resources transitioned away from their default state to barrier them on pass end
967
968 VulkanTextureSubresource *colorAttachmentSubresources[MAX_COLOR_TARGET_BINDINGS];
969 Uint32 colorAttachmentSubresourceCount;
970 VulkanTextureSubresource *resolveAttachmentSubresources[MAX_COLOR_TARGET_BINDINGS];
971 Uint32 resolveAttachmentSubresourceCount;
972
973 VulkanTextureSubresource *depthStencilAttachmentSubresource; // may be NULL
974
975 // Dynamic state
976
977 VkViewport currentViewport;
978 VkRect2D currentScissor;
979 float blendConstants[4];
980 Uint8 stencilRef;
981
982 // Resource bind state
983
984 DescriptorSetCache *descriptorSetCache; // acquired when command buffer is acquired
985
986 bool needNewVertexResourceDescriptorSet;
987 bool needNewVertexUniformDescriptorSet;
988 bool needNewVertexUniformOffsets;
989 bool needNewFragmentResourceDescriptorSet;
990 bool needNewFragmentUniformDescriptorSet;
991 bool needNewFragmentUniformOffsets;
992
993 bool needNewComputeReadOnlyDescriptorSet;
994 bool needNewComputeReadWriteDescriptorSet;
995 bool needNewComputeUniformDescriptorSet;
996 bool needNewComputeUniformOffsets;
997
998 VkDescriptorSet vertexResourceDescriptorSet;
999 VkDescriptorSet vertexUniformDescriptorSet;
1000 VkDescriptorSet fragmentResourceDescriptorSet;
1001 VkDescriptorSet fragmentUniformDescriptorSet;
1002
1003 VkDescriptorSet computeReadOnlyDescriptorSet;
1004 VkDescriptorSet computeReadWriteDescriptorSet;
1005 VkDescriptorSet computeUniformDescriptorSet;
1006
1007 VkBuffer vertexBuffers[MAX_VERTEX_BUFFERS];
1008 VkDeviceSize vertexBufferOffsets[MAX_VERTEX_BUFFERS];
1009 Uint32 vertexBufferCount;
1010 bool needVertexBufferBind;
1011
1012 VulkanTexture *vertexSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE];
1013 VulkanSampler *vertexSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE];
1014 VulkanTexture *vertexStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE];
1015 VulkanBuffer *vertexStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE];
1016
1017 VulkanTexture *fragmentSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE];
1018 VulkanSampler *fragmentSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE];
1019 VulkanTexture *fragmentStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE];
1020 VulkanBuffer *fragmentStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE];
1021
1022 VulkanTextureSubresource *readWriteComputeStorageTextureSubresources[MAX_COMPUTE_WRITE_TEXTURES];
1023 Uint32 readWriteComputeStorageTextureSubresourceCount;
1024 VulkanBuffer *readWriteComputeStorageBuffers[MAX_COMPUTE_WRITE_BUFFERS];
1025
1026 VulkanTexture *computeSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE];
1027 VulkanSampler *computeSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE];
1028 VulkanTexture *readOnlyComputeStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE];
1029 VulkanBuffer *readOnlyComputeStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE];
1030
1031 // Uniform buffers
1032
1033 VulkanUniformBuffer *vertexUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
1034 VulkanUniformBuffer *fragmentUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
1035 VulkanUniformBuffer *computeUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
1036
1037 // Track used resources
1038
1039 VulkanBuffer **usedBuffers;
1040 Sint32 usedBufferCount;
1041 Sint32 usedBufferCapacity;
1042
1043 VulkanTexture **usedTextures;
1044 Sint32 usedTextureCount;
1045 Sint32 usedTextureCapacity;
1046
1047 VulkanSampler **usedSamplers;
1048 Sint32 usedSamplerCount;
1049 Sint32 usedSamplerCapacity;
1050
1051 VulkanGraphicsPipeline **usedGraphicsPipelines;
1052 Sint32 usedGraphicsPipelineCount;
1053 Sint32 usedGraphicsPipelineCapacity;
1054
1055 VulkanComputePipeline **usedComputePipelines;
1056 Sint32 usedComputePipelineCount;
1057 Sint32 usedComputePipelineCapacity;
1058
1059 VulkanFramebuffer **usedFramebuffers;
1060 Sint32 usedFramebufferCount;
1061 Sint32 usedFramebufferCapacity;
1062
1063 VulkanUniformBuffer **usedUniformBuffers;
1064 Sint32 usedUniformBufferCount;
1065 Sint32 usedUniformBufferCapacity;
1066
1067 VulkanFenceHandle *inFlightFence;
1068 bool autoReleaseFence;
1069
1070 bool isDefrag; // Whether this CB was created for defragging
1071} VulkanCommandBuffer;
1072
1073struct VulkanCommandPool
1074{
1075 SDL_ThreadID threadID;
1076 VkCommandPool commandPool;
1077
1078 VulkanCommandBuffer **inactiveCommandBuffers;
1079 Uint32 inactiveCommandBufferCapacity;
1080 Uint32 inactiveCommandBufferCount;
1081};
1082
1083// Context
1084
1085struct VulkanRenderer
1086{
1087 VkInstance instance;
1088 VkPhysicalDevice physicalDevice;
1089 VkPhysicalDeviceProperties2KHR physicalDeviceProperties;
1090 VkPhysicalDeviceDriverPropertiesKHR physicalDeviceDriverProperties;
1091 VkDevice logicalDevice;
1092 Uint8 integratedMemoryNotification;
1093 Uint8 outOfDeviceLocalMemoryWarning;
1094 Uint8 outofBARMemoryWarning;
1095 Uint8 fillModeOnlyWarning;
1096
1097 bool debugMode;
1098 bool preferLowPower;
1099 Uint32 allowedFramesInFlight;
1100
1101 VulkanExtensions supports;
1102 bool supportsDebugUtils;
1103 bool supportsColorspace;
1104 bool supportsFillModeNonSolid;
1105 bool supportsMultiDrawIndirect;
1106
1107 VulkanMemoryAllocator *memoryAllocator;
1108 VkPhysicalDeviceMemoryProperties memoryProperties;
1109 bool checkEmptyAllocations;
1110
1111 WindowData **claimedWindows;
1112 Uint32 claimedWindowCount;
1113 Uint32 claimedWindowCapacity;
1114
1115 Uint32 queueFamilyIndex;
1116 VkQueue unifiedQueue;
1117
1118 VulkanCommandBuffer **submittedCommandBuffers;
1119 Uint32 submittedCommandBufferCount;
1120 Uint32 submittedCommandBufferCapacity;
1121
1122 VulkanFencePool fencePool;
1123
1124 SDL_HashTable *commandPoolHashTable;
1125 SDL_HashTable *renderPassHashTable;
1126 SDL_HashTable *framebufferHashTable;
1127 SDL_HashTable *graphicsPipelineResourceLayoutHashTable;
1128 SDL_HashTable *computePipelineResourceLayoutHashTable;
1129 SDL_HashTable *descriptorSetLayoutHashTable;
1130
1131 VulkanUniformBuffer **uniformBufferPool;
1132 Uint32 uniformBufferPoolCount;
1133 Uint32 uniformBufferPoolCapacity;
1134
1135 DescriptorSetCache **descriptorSetCachePool;
1136 Uint32 descriptorSetCachePoolCount;
1137 Uint32 descriptorSetCachePoolCapacity;
1138
1139 SDL_AtomicInt layoutResourceID;
1140
1141 Uint32 minUBOAlignment;
1142
1143 // Deferred resource destruction
1144
1145 VulkanTexture **texturesToDestroy;
1146 Uint32 texturesToDestroyCount;
1147 Uint32 texturesToDestroyCapacity;
1148
1149 VulkanBuffer **buffersToDestroy;
1150 Uint32 buffersToDestroyCount;
1151 Uint32 buffersToDestroyCapacity;
1152
1153 VulkanSampler **samplersToDestroy;
1154 Uint32 samplersToDestroyCount;
1155 Uint32 samplersToDestroyCapacity;
1156
1157 VulkanGraphicsPipeline **graphicsPipelinesToDestroy;
1158 Uint32 graphicsPipelinesToDestroyCount;
1159 Uint32 graphicsPipelinesToDestroyCapacity;
1160
1161 VulkanComputePipeline **computePipelinesToDestroy;
1162 Uint32 computePipelinesToDestroyCount;
1163 Uint32 computePipelinesToDestroyCapacity;
1164
1165 VulkanShader **shadersToDestroy;
1166 Uint32 shadersToDestroyCount;
1167 Uint32 shadersToDestroyCapacity;
1168
1169 VulkanFramebuffer **framebuffersToDestroy;
1170 Uint32 framebuffersToDestroyCount;
1171 Uint32 framebuffersToDestroyCapacity;
1172
1173 SDL_Mutex *allocatorLock;
1174 SDL_Mutex *disposeLock;
1175 SDL_Mutex *submitLock;
1176 SDL_Mutex *acquireCommandBufferLock;
1177 SDL_Mutex *acquireUniformBufferLock;
1178 SDL_Mutex *framebufferFetchLock;
1179 SDL_Mutex *windowLock;
1180
1181 Uint8 defragInProgress;
1182
1183 VulkanMemoryAllocation **allocationsToDefrag;
1184 Uint32 allocationsToDefragCount;
1185 Uint32 allocationsToDefragCapacity;
1186
1187#define VULKAN_INSTANCE_FUNCTION(func) \
1188 PFN_##func func;
1189#define VULKAN_DEVICE_FUNCTION(func) \
1190 PFN_##func func;
1191#include "SDL_gpu_vulkan_vkfuncs.h"
1192};
1193
1194// Forward declarations
1195
1196static bool VULKAN_INTERNAL_DefragmentMemory(VulkanRenderer *renderer);
1197static bool VULKAN_INTERNAL_BeginCommandBuffer(VulkanRenderer *renderer, VulkanCommandBuffer *commandBuffer);
1198static void VULKAN_ReleaseWindow(SDL_GPURenderer *driverData, SDL_Window *window);
1199static bool VULKAN_Wait(SDL_GPURenderer *driverData);
1200static bool VULKAN_WaitForFences(SDL_GPURenderer *driverData, bool waitAll, SDL_GPUFence *const *fences, Uint32 numFences);
1201static bool VULKAN_Submit(SDL_GPUCommandBuffer *commandBuffer);
1202static SDL_GPUCommandBuffer *VULKAN_AcquireCommandBuffer(SDL_GPURenderer *driverData);
1203
1204// Error Handling
1205
1206static inline const char *VkErrorMessages(VkResult code)
1207{
1208#define ERR_TO_STR(e) \
1209 case e: \
1210 return #e;
1211 switch (code) {
1212 ERR_TO_STR(VK_ERROR_OUT_OF_HOST_MEMORY)
1213 ERR_TO_STR(VK_ERROR_OUT_OF_DEVICE_MEMORY)
1214 ERR_TO_STR(VK_ERROR_FRAGMENTED_POOL)
1215 ERR_TO_STR(VK_ERROR_OUT_OF_POOL_MEMORY)
1216 ERR_TO_STR(VK_ERROR_INITIALIZATION_FAILED)
1217 ERR_TO_STR(VK_ERROR_LAYER_NOT_PRESENT)
1218 ERR_TO_STR(VK_ERROR_EXTENSION_NOT_PRESENT)
1219 ERR_TO_STR(VK_ERROR_FEATURE_NOT_PRESENT)
1220 ERR_TO_STR(VK_ERROR_TOO_MANY_OBJECTS)
1221 ERR_TO_STR(VK_ERROR_DEVICE_LOST)
1222 ERR_TO_STR(VK_ERROR_INCOMPATIBLE_DRIVER)
1223 ERR_TO_STR(VK_ERROR_OUT_OF_DATE_KHR)
1224 ERR_TO_STR(VK_ERROR_SURFACE_LOST_KHR)
1225 ERR_TO_STR(VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT)
1226 ERR_TO_STR(VK_SUBOPTIMAL_KHR)
1227 ERR_TO_STR(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR)
1228 default:
1229 return "Unhandled VkResult!";
1230 }
1231#undef ERR_TO_STR
1232}
1233
1234#define SET_ERROR_AND_RETURN(fmt, msg, ret) \
1235 do { \
1236 if (renderer->debugMode) { \
1237 SDL_LogError(SDL_LOG_CATEGORY_GPU, fmt, msg); \
1238 } \
1239 SDL_SetError((fmt), (msg)); \
1240 return ret; \
1241 } while (0)
1242
1243#define SET_STRING_ERROR_AND_RETURN(msg, ret) SET_ERROR_AND_RETURN("%s", msg, ret)
1244
1245#define CHECK_VULKAN_ERROR_AND_RETURN(res, fn, ret) \
1246 do { \
1247 if ((res) != VK_SUCCESS) { \
1248 if (renderer->debugMode) { \
1249 SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s %s", #fn, VkErrorMessages(res)); \
1250 } \
1251 SDL_SetError("%s %s", #fn, VkErrorMessages(res)); \
1252 return (ret); \
1253 } \
1254 } while (0)
1255
1256// Utility
1257
1258static inline VkPolygonMode SDLToVK_PolygonMode(
1259 VulkanRenderer *renderer,
1260 SDL_GPUFillMode mode)
1261{
1262 if (mode == SDL_GPU_FILLMODE_FILL) {
1263 return VK_POLYGON_MODE_FILL; // always available!
1264 }
1265
1266 if (renderer->supportsFillModeNonSolid && mode == SDL_GPU_FILLMODE_LINE) {
1267 return VK_POLYGON_MODE_LINE;
1268 }
1269
1270 if (!renderer->fillModeOnlyWarning) {
1271 SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Unsupported fill mode requested, using FILL!");
1272 renderer->fillModeOnlyWarning = 1;
1273 }
1274 return VK_POLYGON_MODE_FILL;
1275}
1276
1277// Memory Management
1278
1279// Vulkan: Memory Allocation
1280
1281static inline VkDeviceSize VULKAN_INTERNAL_NextHighestAlignment(
1282 VkDeviceSize n,
1283 VkDeviceSize align)
1284{
1285 return align * ((n + align - 1) / align);
1286}
1287
1288static inline Uint32 VULKAN_INTERNAL_NextHighestAlignment32(
1289 Uint32 n,
1290 Uint32 align)
1291{
1292 return align * ((n + align - 1) / align);
1293}
1294
1295static void VULKAN_INTERNAL_MakeMemoryUnavailable(
1296 VulkanMemoryAllocation *allocation)
1297{
1298 Uint32 i, j;
1299 VulkanMemoryFreeRegion *freeRegion;
1300
1301 allocation->availableForAllocation = 0;
1302
1303 for (i = 0; i < allocation->freeRegionCount; i += 1) {
1304 freeRegion = allocation->freeRegions[i];
1305
1306 // close the gap in the sorted list
1307 if (allocation->allocator->sortedFreeRegionCount > 1) {
1308 for (j = freeRegion->sortedIndex; j < allocation->allocator->sortedFreeRegionCount - 1; j += 1) {
1309 allocation->allocator->sortedFreeRegions[j] =
1310 allocation->allocator->sortedFreeRegions[j + 1];
1311
1312 allocation->allocator->sortedFreeRegions[j]->sortedIndex = j;
1313 }
1314 }
1315
1316 allocation->allocator->sortedFreeRegionCount -= 1;
1317 }
1318}
1319
1320static void VULKAN_INTERNAL_MarkAllocationsForDefrag(
1321 VulkanRenderer *renderer)
1322{
1323 Uint32 memoryType, allocationIndex;
1324 VulkanMemorySubAllocator *currentAllocator;
1325
1326 for (memoryType = 0; memoryType < VK_MAX_MEMORY_TYPES; memoryType += 1) {
1327 currentAllocator = &renderer->memoryAllocator->subAllocators[memoryType];
1328
1329 for (allocationIndex = 0; allocationIndex < currentAllocator->allocationCount; allocationIndex += 1) {
1330 if (currentAllocator->allocations[allocationIndex]->availableForAllocation == 1) {
1331 if (currentAllocator->allocations[allocationIndex]->freeRegionCount > 1) {
1332 EXPAND_ARRAY_IF_NEEDED(
1333 renderer->allocationsToDefrag,
1334 VulkanMemoryAllocation *,
1335 renderer->allocationsToDefragCount + 1,
1336 renderer->allocationsToDefragCapacity,
1337 renderer->allocationsToDefragCapacity * 2);
1338
1339 renderer->allocationsToDefrag[renderer->allocationsToDefragCount] =
1340 currentAllocator->allocations[allocationIndex];
1341
1342 renderer->allocationsToDefragCount += 1;
1343
1344 VULKAN_INTERNAL_MakeMemoryUnavailable(
1345 currentAllocator->allocations[allocationIndex]);
1346 }
1347 }
1348 }
1349 }
1350}
1351
1352static void VULKAN_INTERNAL_RemoveMemoryFreeRegion(
1353 VulkanRenderer *renderer,
1354 VulkanMemoryFreeRegion *freeRegion)
1355{
1356 Uint32 i;
1357
1358 SDL_LockMutex(renderer->allocatorLock);
1359
1360 if (freeRegion->allocation->availableForAllocation) {
1361 // close the gap in the sorted list
1362 if (freeRegion->allocation->allocator->sortedFreeRegionCount > 1) {
1363 for (i = freeRegion->sortedIndex; i < freeRegion->allocation->allocator->sortedFreeRegionCount - 1; i += 1) {
1364 freeRegion->allocation->allocator->sortedFreeRegions[i] =
1365 freeRegion->allocation->allocator->sortedFreeRegions[i + 1];
1366
1367 freeRegion->allocation->allocator->sortedFreeRegions[i]->sortedIndex = i;
1368 }
1369 }
1370
1371 freeRegion->allocation->allocator->sortedFreeRegionCount -= 1;
1372 }
1373
1374 // close the gap in the buffer list
1375 if (freeRegion->allocation->freeRegionCount > 1 && freeRegion->allocationIndex != freeRegion->allocation->freeRegionCount - 1) {
1376 freeRegion->allocation->freeRegions[freeRegion->allocationIndex] =
1377 freeRegion->allocation->freeRegions[freeRegion->allocation->freeRegionCount - 1];
1378
1379 freeRegion->allocation->freeRegions[freeRegion->allocationIndex]->allocationIndex =
1380 freeRegion->allocationIndex;
1381 }
1382
1383 freeRegion->allocation->freeRegionCount -= 1;
1384
1385 freeRegion->allocation->freeSpace -= freeRegion->size;
1386
1387 SDL_free(freeRegion);
1388
1389 SDL_UnlockMutex(renderer->allocatorLock);
1390}
1391
1392static void VULKAN_INTERNAL_NewMemoryFreeRegion(
1393 VulkanRenderer *renderer,
1394 VulkanMemoryAllocation *allocation,
1395 VkDeviceSize offset,
1396 VkDeviceSize size)
1397{
1398 VulkanMemoryFreeRegion *newFreeRegion;
1399 VkDeviceSize newOffset, newSize;
1400 Sint32 insertionIndex = 0;
1401
1402 SDL_LockMutex(renderer->allocatorLock);
1403
1404 // look for an adjacent region to merge
1405 for (Sint32 i = allocation->freeRegionCount - 1; i >= 0; i -= 1) {
1406 // check left side
1407 if (allocation->freeRegions[i]->offset + allocation->freeRegions[i]->size == offset) {
1408 newOffset = allocation->freeRegions[i]->offset;
1409 newSize = allocation->freeRegions[i]->size + size;
1410
1411 VULKAN_INTERNAL_RemoveMemoryFreeRegion(renderer, allocation->freeRegions[i]);
1412 VULKAN_INTERNAL_NewMemoryFreeRegion(renderer, allocation, newOffset, newSize);
1413
1414 SDL_UnlockMutex(renderer->allocatorLock);
1415 return;
1416 }
1417
1418 // check right side
1419 if (allocation->freeRegions[i]->offset == offset + size) {
1420 newOffset = offset;
1421 newSize = allocation->freeRegions[i]->size + size;
1422
1423 VULKAN_INTERNAL_RemoveMemoryFreeRegion(renderer, allocation->freeRegions[i]);
1424 VULKAN_INTERNAL_NewMemoryFreeRegion(renderer, allocation, newOffset, newSize);
1425
1426 SDL_UnlockMutex(renderer->allocatorLock);
1427 return;
1428 }
1429 }
1430
1431 // region is not contiguous with another free region, make a new one
1432 allocation->freeRegionCount += 1;
1433 if (allocation->freeRegionCount > allocation->freeRegionCapacity) {
1434 allocation->freeRegionCapacity *= 2;
1435 allocation->freeRegions = SDL_realloc(
1436 allocation->freeRegions,
1437 sizeof(VulkanMemoryFreeRegion *) * allocation->freeRegionCapacity);
1438 }
1439
1440 newFreeRegion = SDL_malloc(sizeof(VulkanMemoryFreeRegion));
1441 newFreeRegion->offset = offset;
1442 newFreeRegion->size = size;
1443 newFreeRegion->allocation = allocation;
1444
1445 allocation->freeSpace += size;
1446
1447 allocation->freeRegions[allocation->freeRegionCount - 1] = newFreeRegion;
1448 newFreeRegion->allocationIndex = allocation->freeRegionCount - 1;
1449
1450 if (allocation->availableForAllocation) {
1451 for (Uint32 i = 0; i < allocation->allocator->sortedFreeRegionCount; i += 1) {
1452 if (allocation->allocator->sortedFreeRegions[i]->size < size) {
1453 // this is where the new region should go
1454 break;
1455 }
1456
1457 insertionIndex += 1;
1458 }
1459
1460 if (allocation->allocator->sortedFreeRegionCount + 1 > allocation->allocator->sortedFreeRegionCapacity) {
1461 allocation->allocator->sortedFreeRegionCapacity *= 2;
1462 allocation->allocator->sortedFreeRegions = SDL_realloc(
1463 allocation->allocator->sortedFreeRegions,
1464 sizeof(VulkanMemoryFreeRegion *) * allocation->allocator->sortedFreeRegionCapacity);
1465 }
1466
1467 // perform insertion sort
1468 if (allocation->allocator->sortedFreeRegionCount > 0 && (Uint32)insertionIndex != allocation->allocator->sortedFreeRegionCount) {
1469 for (Sint32 i = allocation->allocator->sortedFreeRegionCount; i > insertionIndex && i > 0; i -= 1) {
1470 allocation->allocator->sortedFreeRegions[i] = allocation->allocator->sortedFreeRegions[i - 1];
1471 allocation->allocator->sortedFreeRegions[i]->sortedIndex = i;
1472 }
1473 }
1474
1475 allocation->allocator->sortedFreeRegionCount += 1;
1476 allocation->allocator->sortedFreeRegions[insertionIndex] = newFreeRegion;
1477 newFreeRegion->sortedIndex = insertionIndex;
1478 }
1479
1480 SDL_UnlockMutex(renderer->allocatorLock);
1481}
1482
1483static VulkanMemoryUsedRegion *VULKAN_INTERNAL_NewMemoryUsedRegion(
1484 VulkanRenderer *renderer,
1485 VulkanMemoryAllocation *allocation,
1486 VkDeviceSize offset,
1487 VkDeviceSize size,
1488 VkDeviceSize resourceOffset,
1489 VkDeviceSize resourceSize,
1490 VkDeviceSize alignment)
1491{
1492 VulkanMemoryUsedRegion *memoryUsedRegion;
1493
1494 SDL_LockMutex(renderer->allocatorLock);
1495
1496 if (allocation->usedRegionCount == allocation->usedRegionCapacity) {
1497 allocation->usedRegionCapacity *= 2;
1498 allocation->usedRegions = SDL_realloc(
1499 allocation->usedRegions,
1500 allocation->usedRegionCapacity * sizeof(VulkanMemoryUsedRegion *));
1501 }
1502
1503 memoryUsedRegion = SDL_malloc(sizeof(VulkanMemoryUsedRegion));
1504 memoryUsedRegion->allocation = allocation;
1505 memoryUsedRegion->offset = offset;
1506 memoryUsedRegion->size = size;
1507 memoryUsedRegion->resourceOffset = resourceOffset;
1508 memoryUsedRegion->resourceSize = resourceSize;
1509 memoryUsedRegion->alignment = alignment;
1510
1511 allocation->usedSpace += size;
1512
1513 allocation->usedRegions[allocation->usedRegionCount] = memoryUsedRegion;
1514 allocation->usedRegionCount += 1;
1515
1516 SDL_UnlockMutex(renderer->allocatorLock);
1517
1518 return memoryUsedRegion;
1519}
1520
1521static void VULKAN_INTERNAL_RemoveMemoryUsedRegion(
1522 VulkanRenderer *renderer,
1523 VulkanMemoryUsedRegion *usedRegion)
1524{
1525 Uint32 i;
1526
1527 SDL_LockMutex(renderer->allocatorLock);
1528
1529 for (i = 0; i < usedRegion->allocation->usedRegionCount; i += 1) {
1530 if (usedRegion->allocation->usedRegions[i] == usedRegion) {
1531 // plug the hole
1532 if (i != usedRegion->allocation->usedRegionCount - 1) {
1533 usedRegion->allocation->usedRegions[i] = usedRegion->allocation->usedRegions[usedRegion->allocation->usedRegionCount - 1];
1534 }
1535
1536 break;
1537 }
1538 }
1539
1540 usedRegion->allocation->usedSpace -= usedRegion->size;
1541
1542 usedRegion->allocation->usedRegionCount -= 1;
1543
1544 VULKAN_INTERNAL_NewMemoryFreeRegion(
1545 renderer,
1546 usedRegion->allocation,
1547 usedRegion->offset,
1548 usedRegion->size);
1549
1550 if (usedRegion->allocation->usedRegionCount == 0) {
1551 renderer->checkEmptyAllocations = true;
1552 }
1553
1554 SDL_free(usedRegion);
1555
1556 SDL_UnlockMutex(renderer->allocatorLock);
1557}
1558
1559static bool VULKAN_INTERNAL_CheckMemoryTypeArrayUnique(
1560 Uint32 memoryTypeIndex,
1561 const Uint32 *memoryTypeIndexArray,
1562 Uint32 count)
1563{
1564 Uint32 i = 0;
1565
1566 for (i = 0; i < count; i += 1) {
1567 if (memoryTypeIndexArray[i] == memoryTypeIndex) {
1568 return false;
1569 }
1570 }
1571
1572 return true;
1573}
1574
1575/* Returns an array of memory type indices in order of preference.
1576 * Memory types are requested with the following three guidelines:
1577 *
1578 * Required: Absolutely necessary
1579 * Preferred: Nice to have, but not necessary
1580 * Tolerable: Can be allowed if there are no other options
1581 *
1582 * We return memory types in this order:
1583 * 1. Required and preferred. This is the best category.
1584 * 2. Required only.
1585 * 3. Required, preferred, and tolerable.
1586 * 4. Required and tolerable. This is the worst category.
1587 */
1588static Uint32 *VULKAN_INTERNAL_FindBestMemoryTypes(
1589 VulkanRenderer *renderer,
1590 Uint32 typeFilter,
1591 VkMemoryPropertyFlags requiredProperties,
1592 VkMemoryPropertyFlags preferredProperties,
1593 VkMemoryPropertyFlags tolerableProperties,
1594 Uint32 *pCount)
1595{
1596 Uint32 i;
1597 Uint32 index = 0;
1598 Uint32 *result = SDL_malloc(sizeof(Uint32) * renderer->memoryProperties.memoryTypeCount);
1599
1600 // required + preferred + !tolerable
1601 for (i = 0; i < renderer->memoryProperties.memoryTypeCount; i += 1) {
1602 if ((typeFilter & (1 << i)) &&
1603 (renderer->memoryProperties.memoryTypes[i].propertyFlags & requiredProperties) == requiredProperties &&
1604 (renderer->memoryProperties.memoryTypes[i].propertyFlags & preferredProperties) == preferredProperties &&
1605 (renderer->memoryProperties.memoryTypes[i].propertyFlags & tolerableProperties) == 0) {
1606 if (VULKAN_INTERNAL_CheckMemoryTypeArrayUnique(
1607 i,
1608 result,
1609 index)) {
1610 result[index] = i;
1611 index += 1;
1612 }
1613 }
1614 }
1615
1616 // required + !preferred + !tolerable
1617 for (i = 0; i < renderer->memoryProperties.memoryTypeCount; i += 1) {
1618 if ((typeFilter & (1 << i)) &&
1619 (renderer->memoryProperties.memoryTypes[i].propertyFlags & requiredProperties) == requiredProperties &&
1620 (renderer->memoryProperties.memoryTypes[i].propertyFlags & preferredProperties) == 0 &&
1621 (renderer->memoryProperties.memoryTypes[i].propertyFlags & tolerableProperties) == 0) {
1622 if (VULKAN_INTERNAL_CheckMemoryTypeArrayUnique(
1623 i,
1624 result,
1625 index)) {
1626 result[index] = i;
1627 index += 1;
1628 }
1629 }
1630 }
1631
1632 // required + preferred + tolerable
1633 for (i = 0; i < renderer->memoryProperties.memoryTypeCount; i += 1) {
1634 if ((typeFilter & (1 << i)) &&
1635 (renderer->memoryProperties.memoryTypes[i].propertyFlags & requiredProperties) == requiredProperties &&
1636 (renderer->memoryProperties.memoryTypes[i].propertyFlags & preferredProperties) == preferredProperties &&
1637 (renderer->memoryProperties.memoryTypes[i].propertyFlags & tolerableProperties) == tolerableProperties) {
1638 if (VULKAN_INTERNAL_CheckMemoryTypeArrayUnique(
1639 i,
1640 result,
1641 index)) {
1642 result[index] = i;
1643 index += 1;
1644 }
1645 }
1646 }
1647
1648 // required + !preferred + tolerable
1649 for (i = 0; i < renderer->memoryProperties.memoryTypeCount; i += 1) {
1650 if ((typeFilter & (1 << i)) &&
1651 (renderer->memoryProperties.memoryTypes[i].propertyFlags & requiredProperties) == requiredProperties &&
1652 (renderer->memoryProperties.memoryTypes[i].propertyFlags & preferredProperties) == 0 &&
1653 (renderer->memoryProperties.memoryTypes[i].propertyFlags & tolerableProperties) == tolerableProperties) {
1654 if (VULKAN_INTERNAL_CheckMemoryTypeArrayUnique(
1655 i,
1656 result,
1657 index)) {
1658 result[index] = i;
1659 index += 1;
1660 }
1661 }
1662 }
1663
1664 *pCount = index;
1665 return result;
1666}
1667
1668static Uint32 *VULKAN_INTERNAL_FindBestBufferMemoryTypes(
1669 VulkanRenderer *renderer,
1670 VkBuffer buffer,
1671 VkMemoryPropertyFlags requiredMemoryProperties,
1672 VkMemoryPropertyFlags preferredMemoryProperties,
1673 VkMemoryPropertyFlags tolerableMemoryProperties,
1674 VkMemoryRequirements *pMemoryRequirements,
1675 Uint32 *pCount)
1676{
1677 renderer->vkGetBufferMemoryRequirements(
1678 renderer->logicalDevice,
1679 buffer,
1680 pMemoryRequirements);
1681
1682 return VULKAN_INTERNAL_FindBestMemoryTypes(
1683 renderer,
1684 pMemoryRequirements->memoryTypeBits,
1685 requiredMemoryProperties,
1686 preferredMemoryProperties,
1687 tolerableMemoryProperties,
1688 pCount);
1689}
1690
1691static Uint32 *VULKAN_INTERNAL_FindBestImageMemoryTypes(
1692 VulkanRenderer *renderer,
1693 VkImage image,
1694 VkMemoryPropertyFlags preferredMemoryPropertyFlags,
1695 VkMemoryRequirements *pMemoryRequirements,
1696 Uint32 *pCount)
1697{
1698 renderer->vkGetImageMemoryRequirements(
1699 renderer->logicalDevice,
1700 image,
1701 pMemoryRequirements);
1702
1703 return VULKAN_INTERNAL_FindBestMemoryTypes(
1704 renderer,
1705 pMemoryRequirements->memoryTypeBits,
1706 0,
1707 preferredMemoryPropertyFlags,
1708 0,
1709 pCount);
1710}
1711
1712static void VULKAN_INTERNAL_DeallocateMemory(
1713 VulkanRenderer *renderer,
1714 VulkanMemorySubAllocator *allocator,
1715 Uint32 allocationIndex)
1716{
1717 Uint32 i;
1718
1719 VulkanMemoryAllocation *allocation = allocator->allocations[allocationIndex];
1720
1721 SDL_LockMutex(renderer->allocatorLock);
1722
1723 // If this allocation was marked for defrag, cancel that
1724 for (i = 0; i < renderer->allocationsToDefragCount; i += 1) {
1725 if (allocation == renderer->allocationsToDefrag[i]) {
1726 renderer->allocationsToDefrag[i] = renderer->allocationsToDefrag[renderer->allocationsToDefragCount - 1];
1727 renderer->allocationsToDefragCount -= 1;
1728
1729 break;
1730 }
1731 }
1732
1733 for (i = 0; i < allocation->freeRegionCount; i += 1) {
1734 VULKAN_INTERNAL_RemoveMemoryFreeRegion(
1735 renderer,
1736 allocation->freeRegions[i]);
1737 }
1738 SDL_free(allocation->freeRegions);
1739
1740 /* no need to iterate used regions because deallocate
1741 * only happens when there are 0 used regions
1742 */
1743 SDL_free(allocation->usedRegions);
1744
1745 renderer->vkFreeMemory(
1746 renderer->logicalDevice,
1747 allocation->memory,
1748 NULL);
1749
1750 SDL_DestroyMutex(allocation->memoryLock);
1751 SDL_free(allocation);
1752
1753 if (allocationIndex != allocator->allocationCount - 1) {
1754 allocator->allocations[allocationIndex] = allocator->allocations[allocator->allocationCount - 1];
1755 }
1756
1757 allocator->allocationCount -= 1;
1758
1759 SDL_UnlockMutex(renderer->allocatorLock);
1760}
1761
1762static Uint8 VULKAN_INTERNAL_AllocateMemory(
1763 VulkanRenderer *renderer,
1764 Uint32 memoryTypeIndex,
1765 VkDeviceSize allocationSize,
1766 Uint8 isHostVisible,
1767 VulkanMemoryAllocation **pMemoryAllocation)
1768{
1769 VulkanMemoryAllocation *allocation;
1770 VulkanMemorySubAllocator *allocator = &renderer->memoryAllocator->subAllocators[memoryTypeIndex];
1771 VkMemoryAllocateInfo allocInfo;
1772 VkResult result;
1773
1774 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1775 allocInfo.pNext = NULL;
1776 allocInfo.memoryTypeIndex = memoryTypeIndex;
1777 allocInfo.allocationSize = allocationSize;
1778
1779 allocation = SDL_malloc(sizeof(VulkanMemoryAllocation));
1780 allocation->size = allocationSize;
1781 allocation->freeSpace = 0; // added by FreeRegions
1782 allocation->usedSpace = 0; // added by UsedRegions
1783 allocation->memoryLock = SDL_CreateMutex();
1784
1785 allocator->allocationCount += 1;
1786 allocator->allocations = SDL_realloc(
1787 allocator->allocations,
1788 sizeof(VulkanMemoryAllocation *) * allocator->allocationCount);
1789
1790 allocator->allocations[allocator->allocationCount - 1] = allocation;
1791
1792 allocInfo.pNext = NULL;
1793 allocation->availableForAllocation = 1;
1794
1795 allocation->usedRegions = SDL_malloc(sizeof(VulkanMemoryUsedRegion *));
1796 allocation->usedRegionCount = 0;
1797 allocation->usedRegionCapacity = 1;
1798
1799 allocation->freeRegions = SDL_malloc(sizeof(VulkanMemoryFreeRegion *));
1800 allocation->freeRegionCount = 0;
1801 allocation->freeRegionCapacity = 1;
1802
1803 allocation->allocator = allocator;
1804
1805 result = renderer->vkAllocateMemory(
1806 renderer->logicalDevice,
1807 &allocInfo,
1808 NULL,
1809 &allocation->memory);
1810
1811 if (result != VK_SUCCESS) {
1812 // Uh oh, we couldn't allocate, time to clean up
1813 SDL_free(allocation->freeRegions);
1814
1815 allocator->allocationCount -= 1;
1816 allocator->allocations = SDL_realloc(
1817 allocator->allocations,
1818 sizeof(VulkanMemoryAllocation *) * allocator->allocationCount);
1819
1820 SDL_free(allocation);
1821
1822 return 0;
1823 }
1824
1825 // Persistent mapping for host-visible memory
1826 if (isHostVisible) {
1827 result = renderer->vkMapMemory(
1828 renderer->logicalDevice,
1829 allocation->memory,
1830 0,
1831 VK_WHOLE_SIZE,
1832 0,
1833 (void **)&allocation->mapPointer);
1834 CHECK_VULKAN_ERROR_AND_RETURN(result, vkMapMemory, 0);
1835 } else {
1836 allocation->mapPointer = NULL;
1837 }
1838
1839 VULKAN_INTERNAL_NewMemoryFreeRegion(
1840 renderer,
1841 allocation,
1842 0,
1843 allocation->size);
1844
1845 *pMemoryAllocation = allocation;
1846 return 1;
1847}
1848
1849static Uint8 VULKAN_INTERNAL_BindBufferMemory(
1850 VulkanRenderer *renderer,
1851 VulkanMemoryUsedRegion *usedRegion,
1852 VkDeviceSize alignedOffset,
1853 VkBuffer buffer)
1854{
1855 VkResult vulkanResult;
1856
1857 SDL_LockMutex(usedRegion->allocation->memoryLock);
1858
1859 vulkanResult = renderer->vkBindBufferMemory(
1860 renderer->logicalDevice,
1861 buffer,
1862 usedRegion->allocation->memory,
1863 alignedOffset);
1864
1865 SDL_UnlockMutex(usedRegion->allocation->memoryLock);
1866
1867 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkBindBufferMemory, 0);
1868
1869 return 1;
1870}
1871
1872static Uint8 VULKAN_INTERNAL_BindImageMemory(
1873 VulkanRenderer *renderer,
1874 VulkanMemoryUsedRegion *usedRegion,
1875 VkDeviceSize alignedOffset,
1876 VkImage image)
1877{
1878 VkResult vulkanResult;
1879
1880 SDL_LockMutex(usedRegion->allocation->memoryLock);
1881
1882 vulkanResult = renderer->vkBindImageMemory(
1883 renderer->logicalDevice,
1884 image,
1885 usedRegion->allocation->memory,
1886 alignedOffset);
1887
1888 SDL_UnlockMutex(usedRegion->allocation->memoryLock);
1889
1890 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkBindImageMemory, 0);
1891
1892 return 1;
1893}
1894
1895static Uint8 VULKAN_INTERNAL_BindResourceMemory(
1896 VulkanRenderer *renderer,
1897 Uint32 memoryTypeIndex,
1898 VkMemoryRequirements *memoryRequirements,
1899 VkDeviceSize resourceSize, // may be different from requirements size!
1900 bool dedicated, // the entire memory allocation should be used for this resource
1901 VkBuffer buffer, // may be VK_NULL_HANDLE
1902 VkImage image, // may be VK_NULL_HANDLE
1903 VulkanMemoryUsedRegion **pMemoryUsedRegion)
1904{
1905 VulkanMemoryAllocation *allocation;
1906 VulkanMemorySubAllocator *allocator;
1907 VulkanMemoryFreeRegion *region;
1908 VulkanMemoryFreeRegion *selectedRegion;
1909 VulkanMemoryUsedRegion *usedRegion;
1910
1911 VkDeviceSize requiredSize, allocationSize;
1912 VkDeviceSize alignedOffset = 0;
1913 VkDeviceSize newRegionSize, newRegionOffset;
1914 Uint8 isHostVisible, smallAllocation, allocationResult;
1915 Sint32 i;
1916
1917 isHostVisible =
1918 (renderer->memoryProperties.memoryTypes[memoryTypeIndex].propertyFlags &
1919 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
1920
1921 allocator = &renderer->memoryAllocator->subAllocators[memoryTypeIndex];
1922 requiredSize = memoryRequirements->size;
1923 smallAllocation = requiredSize <= SMALL_ALLOCATION_THRESHOLD;
1924
1925 if ((buffer == VK_NULL_HANDLE && image == VK_NULL_HANDLE) ||
1926 (buffer != VK_NULL_HANDLE && image != VK_NULL_HANDLE)) {
1927 SDL_LogError(SDL_LOG_CATEGORY_GPU, "BindResourceMemory must be given either a VulkanBuffer or a VulkanTexture");
1928 return 0;
1929 }
1930
1931 SDL_LockMutex(renderer->allocatorLock);
1932
1933 selectedRegion = NULL;
1934
1935 if (dedicated) {
1936 // Force an allocation
1937 allocationSize = requiredSize;
1938 } else {
1939 // Search for a suitable existing free region
1940 for (i = allocator->sortedFreeRegionCount - 1; i >= 0; i -= 1) {
1941 region = allocator->sortedFreeRegions[i];
1942
1943 if (smallAllocation && region->allocation->size != SMALL_ALLOCATION_SIZE) {
1944 // region is not in a small allocation
1945 continue;
1946 }
1947
1948 if (!smallAllocation && region->allocation->size == SMALL_ALLOCATION_SIZE) {
1949 // allocation is not small and current region is in a small allocation
1950 continue;
1951 }
1952
1953 alignedOffset = VULKAN_INTERNAL_NextHighestAlignment(
1954 region->offset,
1955 memoryRequirements->alignment);
1956
1957 if (alignedOffset + requiredSize <= region->offset + region->size) {
1958 selectedRegion = region;
1959 break;
1960 }
1961 }
1962
1963 if (selectedRegion != NULL) {
1964 region = selectedRegion;
1965 allocation = region->allocation;
1966
1967 usedRegion = VULKAN_INTERNAL_NewMemoryUsedRegion(
1968 renderer,
1969 allocation,
1970 region->offset,
1971 requiredSize + (alignedOffset - region->offset),
1972 alignedOffset,
1973 resourceSize,
1974 memoryRequirements->alignment);
1975
1976 usedRegion->isBuffer = buffer != VK_NULL_HANDLE;
1977
1978 newRegionSize = region->size - ((alignedOffset - region->offset) + requiredSize);
1979 newRegionOffset = alignedOffset + requiredSize;
1980
1981 // remove and add modified region to re-sort
1982 VULKAN_INTERNAL_RemoveMemoryFreeRegion(renderer, region);
1983
1984 // if size is 0, no need to re-insert
1985 if (newRegionSize != 0) {
1986 VULKAN_INTERNAL_NewMemoryFreeRegion(
1987 renderer,
1988 allocation,
1989 newRegionOffset,
1990 newRegionSize);
1991 }
1992
1993 SDL_UnlockMutex(renderer->allocatorLock);
1994
1995 if (buffer != VK_NULL_HANDLE) {
1996 if (!VULKAN_INTERNAL_BindBufferMemory(
1997 renderer,
1998 usedRegion,
1999 alignedOffset,
2000 buffer)) {
2001 VULKAN_INTERNAL_RemoveMemoryUsedRegion(
2002 renderer,
2003 usedRegion);
2004
2005 return 0;
2006 }
2007 } else if (image != VK_NULL_HANDLE) {
2008 if (!VULKAN_INTERNAL_BindImageMemory(
2009 renderer,
2010 usedRegion,
2011 alignedOffset,
2012 image)) {
2013 VULKAN_INTERNAL_RemoveMemoryUsedRegion(
2014 renderer,
2015 usedRegion);
2016
2017 return 0;
2018 }
2019 }
2020
2021 *pMemoryUsedRegion = usedRegion;
2022 return 1;
2023 }
2024
2025 // No suitable free regions exist, allocate a new memory region
2026 if (
2027 renderer->allocationsToDefragCount == 0 &&
2028 !renderer->defragInProgress) {
2029 // Mark currently fragmented allocations for defrag
2030 VULKAN_INTERNAL_MarkAllocationsForDefrag(renderer);
2031 }
2032
2033 if (requiredSize > SMALL_ALLOCATION_THRESHOLD) {
2034 // allocate a page of required size aligned to LARGE_ALLOCATION_INCREMENT increments
2035 allocationSize =
2036 VULKAN_INTERNAL_NextHighestAlignment(requiredSize, LARGE_ALLOCATION_INCREMENT);
2037 } else {
2038 allocationSize = SMALL_ALLOCATION_SIZE;
2039 }
2040 }
2041
2042 allocationResult = VULKAN_INTERNAL_AllocateMemory(
2043 renderer,
2044 memoryTypeIndex,
2045 allocationSize,
2046 isHostVisible,
2047 &allocation);
2048
2049 // Uh oh, we're out of memory
2050 if (allocationResult == 0) {
2051 SDL_UnlockMutex(renderer->allocatorLock);
2052
2053 // Responsibility of the caller to handle being out of memory
2054 return 2;
2055 }
2056
2057 usedRegion = VULKAN_INTERNAL_NewMemoryUsedRegion(
2058 renderer,
2059 allocation,
2060 0,
2061 requiredSize,
2062 0,
2063 resourceSize,
2064 memoryRequirements->alignment);
2065
2066 usedRegion->isBuffer = buffer != VK_NULL_HANDLE;
2067
2068 region = allocation->freeRegions[0];
2069
2070 newRegionOffset = region->offset + requiredSize;
2071 newRegionSize = region->size - requiredSize;
2072
2073 VULKAN_INTERNAL_RemoveMemoryFreeRegion(renderer, region);
2074
2075 if (newRegionSize != 0) {
2076 VULKAN_INTERNAL_NewMemoryFreeRegion(
2077 renderer,
2078 allocation,
2079 newRegionOffset,
2080 newRegionSize);
2081 }
2082
2083 SDL_UnlockMutex(renderer->allocatorLock);
2084
2085 if (buffer != VK_NULL_HANDLE) {
2086 if (!VULKAN_INTERNAL_BindBufferMemory(
2087 renderer,
2088 usedRegion,
2089 0,
2090 buffer)) {
2091 VULKAN_INTERNAL_RemoveMemoryUsedRegion(
2092 renderer,
2093 usedRegion);
2094
2095 return 0;
2096 }
2097 } else if (image != VK_NULL_HANDLE) {
2098 if (!VULKAN_INTERNAL_BindImageMemory(
2099 renderer,
2100 usedRegion,
2101 0,
2102 image)) {
2103 VULKAN_INTERNAL_RemoveMemoryUsedRegion(
2104 renderer,
2105 usedRegion);
2106
2107 return 0;
2108 }
2109 }
2110
2111 *pMemoryUsedRegion = usedRegion;
2112 return 1;
2113}
2114
2115static Uint8 VULKAN_INTERNAL_BindMemoryForImage(
2116 VulkanRenderer *renderer,
2117 VkImage image,
2118 VulkanMemoryUsedRegion **usedRegion)
2119{
2120 Uint8 bindResult = 0;
2121 Uint32 memoryTypeCount = 0;
2122 Uint32 *memoryTypesToTry = NULL;
2123 Uint32 selectedMemoryTypeIndex = 0;
2124 Uint32 i;
2125 VkMemoryPropertyFlags preferredMemoryPropertyFlags;
2126 VkMemoryRequirements memoryRequirements;
2127
2128 /* Vulkan memory types have several memory properties.
2129 *
2130 * Unlike buffers, images are always optimally stored device-local,
2131 * so that is the only property we prefer here.
2132 *
2133 * If memory is constrained, it is fine for the texture to not
2134 * be device-local.
2135 */
2136 preferredMemoryPropertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
2137
2138 memoryTypesToTry = VULKAN_INTERNAL_FindBestImageMemoryTypes(
2139 renderer,
2140 image,
2141 preferredMemoryPropertyFlags,
2142 &memoryRequirements,
2143 &memoryTypeCount);
2144
2145 for (i = 0; i < memoryTypeCount; i += 1) {
2146 bindResult = VULKAN_INTERNAL_BindResourceMemory(
2147 renderer,
2148 memoryTypesToTry[i],
2149 &memoryRequirements,
2150 memoryRequirements.size,
2151 false,
2152 VK_NULL_HANDLE,
2153 image,
2154 usedRegion);
2155
2156 if (bindResult == 1) {
2157 selectedMemoryTypeIndex = memoryTypesToTry[i];
2158 break;
2159 }
2160 }
2161
2162 SDL_free(memoryTypesToTry);
2163
2164 // Check for warnings on success
2165 if (bindResult == 1) {
2166 if (!renderer->outOfDeviceLocalMemoryWarning) {
2167 if ((renderer->memoryProperties.memoryTypes[selectedMemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0) {
2168 SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Out of device-local memory, allocating textures on host-local memory!");
2169 renderer->outOfDeviceLocalMemoryWarning = 1;
2170 }
2171 }
2172 }
2173
2174 return bindResult;
2175}
2176
2177static Uint8 VULKAN_INTERNAL_BindMemoryForBuffer(
2178 VulkanRenderer *renderer,
2179 VkBuffer buffer,
2180 VkDeviceSize size,
2181 VulkanBufferType type,
2182 bool dedicated,
2183 VulkanMemoryUsedRegion **usedRegion)
2184{
2185 Uint8 bindResult = 0;
2186 Uint32 memoryTypeCount = 0;
2187 Uint32 *memoryTypesToTry = NULL;
2188 Uint32 selectedMemoryTypeIndex = 0;
2189 Uint32 i;
2190 VkMemoryPropertyFlags requiredMemoryPropertyFlags = 0;
2191 VkMemoryPropertyFlags preferredMemoryPropertyFlags = 0;
2192 VkMemoryPropertyFlags tolerableMemoryPropertyFlags = 0;
2193 VkMemoryRequirements memoryRequirements;
2194
2195 /* Buffers need to be optimally bound to a memory type
2196 * based on their use case and the architecture of the system.
2197 *
2198 * It is important to understand the distinction between device and host.
2199 *
2200 * On a traditional high-performance desktop computer,
2201 * the "device" would be the GPU, and the "host" would be the CPU.
2202 * Memory being copied between these two must cross the PCI bus.
2203 * On these systems we have to be concerned about bandwidth limitations
2204 * and causing memory stalls, so we have taken a great deal of care
2205 * to structure this API to guide the client towards optimal usage.
2206 *
2207 * Other kinds of devices do not necessarily have this distinction.
2208 * On an iPhone or Nintendo Switch, all memory is accessible both to the
2209 * GPU and the CPU at all times. These kinds of systems are known as
2210 * UMA, or Unified Memory Architecture. A desktop computer using the
2211 * CPU's integrated graphics can also be thought of as UMA.
2212 *
2213 * Vulkan memory types have several memory properties.
2214 * The relevant memory properties are as follows:
2215 *
2216 * DEVICE_LOCAL:
2217 * This memory is on-device and most efficient for device access.
2218 * On UMA systems all memory is device-local.
2219 * If memory is not device-local, then it is host-local.
2220 *
2221 * HOST_VISIBLE:
2222 * This memory can be mapped for host access, meaning we can obtain
2223 * a pointer to directly access the memory.
2224 *
2225 * HOST_COHERENT:
2226 * Host-coherent memory does not require cache management operations
2227 * when mapped, so we always set this alongside HOST_VISIBLE
2228 * to avoid extra record keeping.
2229 *
2230 * HOST_CACHED:
2231 * Host-cached memory is faster to access than uncached memory
2232 * but memory of this type might not always be available.
2233 *
2234 * GPU buffers, like vertex buffers, indirect buffers, etc
2235 * are optimally stored in device-local memory.
2236 * However, if device-local memory is low, these buffers
2237 * can be accessed from host-local memory with a performance penalty.
2238 *
2239 * Uniform buffers must be host-visible and coherent because
2240 * the client uses them to quickly push small amounts of data.
2241 * We prefer uniform buffers to also be device-local because
2242 * they are accessed by shaders, but the amount of memory
2243 * that is both device-local and host-visible
2244 * is often constrained, particularly on low-end devices.
2245 *
2246 * Transfer buffers must be host-visible and coherent because
2247 * the client uses them to stage data to be transferred
2248 * to device-local memory, or to read back data transferred
2249 * from the device. We prefer the cache bit for performance
2250 * but it isn't strictly necessary. We tolerate device-local
2251 * memory in this situation because, as mentioned above,
2252 * on certain devices all memory is device-local, and even
2253 * though the transfer isn't strictly necessary it is still
2254 * useful for correctly timelining data.
2255 */
2256 if (type == VULKAN_BUFFER_TYPE_GPU) {
2257 preferredMemoryPropertyFlags |=
2258 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
2259 } else if (type == VULKAN_BUFFER_TYPE_UNIFORM) {
2260 requiredMemoryPropertyFlags |=
2261 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
2262 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
2263
2264 preferredMemoryPropertyFlags |=
2265 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
2266 } else if (type == VULKAN_BUFFER_TYPE_TRANSFER) {
2267 requiredMemoryPropertyFlags |=
2268 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
2269 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
2270
2271 preferredMemoryPropertyFlags |=
2272 VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
2273
2274 tolerableMemoryPropertyFlags |=
2275 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
2276 } else {
2277 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized buffer type!");
2278 return 0;
2279 }
2280
2281 memoryTypesToTry = VULKAN_INTERNAL_FindBestBufferMemoryTypes(
2282 renderer,
2283 buffer,
2284 requiredMemoryPropertyFlags,
2285 preferredMemoryPropertyFlags,
2286 tolerableMemoryPropertyFlags,
2287 &memoryRequirements,
2288 &memoryTypeCount);
2289
2290 for (i = 0; i < memoryTypeCount; i += 1) {
2291 bindResult = VULKAN_INTERNAL_BindResourceMemory(
2292 renderer,
2293 memoryTypesToTry[i],
2294 &memoryRequirements,
2295 size,
2296 dedicated,
2297 buffer,
2298 VK_NULL_HANDLE,
2299 usedRegion);
2300
2301 if (bindResult == 1) {
2302 selectedMemoryTypeIndex = memoryTypesToTry[i];
2303 break;
2304 }
2305 }
2306
2307 SDL_free(memoryTypesToTry);
2308
2309 // Check for warnings on success
2310 if (bindResult == 1) {
2311 if (type == VULKAN_BUFFER_TYPE_GPU) {
2312 if (!renderer->outOfDeviceLocalMemoryWarning) {
2313 if ((renderer->memoryProperties.memoryTypes[selectedMemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0) {
2314 SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Out of device-local memory, allocating buffers on host-local memory, expect degraded performance!");
2315 renderer->outOfDeviceLocalMemoryWarning = 1;
2316 }
2317 }
2318 } else if (type == VULKAN_BUFFER_TYPE_UNIFORM) {
2319 if (!renderer->outofBARMemoryWarning) {
2320 if ((renderer->memoryProperties.memoryTypes[selectedMemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0) {
2321 SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Out of BAR memory, allocating uniform buffers on host-local memory, expect degraded performance!");
2322 renderer->outofBARMemoryWarning = 1;
2323 }
2324 }
2325 } else if (type == VULKAN_BUFFER_TYPE_TRANSFER) {
2326 if (!renderer->integratedMemoryNotification) {
2327 if ((renderer->memoryProperties.memoryTypes[selectedMemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
2328 SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Integrated memory detected, allocating TransferBuffers on device-local memory!");
2329 renderer->integratedMemoryNotification = 1;
2330 }
2331 }
2332 }
2333 }
2334
2335 return bindResult;
2336}
2337
2338// Resource tracking
2339
2340#define TRACK_RESOURCE(resource, type, array, count, capacity) \
2341 for (Sint32 i = commandBuffer->count - 1; i >= 0; i -= 1) { \
2342 if (commandBuffer->array[i] == resource) { \
2343 return; \
2344 } \
2345 } \
2346 \
2347 if (commandBuffer->count == commandBuffer->capacity) { \
2348 commandBuffer->capacity += 1; \
2349 commandBuffer->array = SDL_realloc( \
2350 commandBuffer->array, \
2351 commandBuffer->capacity * sizeof(type)); \
2352 } \
2353 commandBuffer->array[commandBuffer->count] = resource; \
2354 commandBuffer->count += 1; \
2355 SDL_AtomicIncRef(&resource->referenceCount);
2356
2357static void VULKAN_INTERNAL_TrackBuffer(
2358 VulkanCommandBuffer *commandBuffer,
2359 VulkanBuffer *buffer)
2360{
2361 TRACK_RESOURCE(
2362 buffer,
2363 VulkanBuffer *,
2364 usedBuffers,
2365 usedBufferCount,
2366 usedBufferCapacity)
2367}
2368
2369static void VULKAN_INTERNAL_TrackTexture(
2370 VulkanCommandBuffer *commandBuffer,
2371 VulkanTexture *texture)
2372{
2373 TRACK_RESOURCE(
2374 texture,
2375 VulkanTexture *,
2376 usedTextures,
2377 usedTextureCount,
2378 usedTextureCapacity)
2379}
2380
2381static void VULKAN_INTERNAL_TrackSampler(
2382 VulkanCommandBuffer *commandBuffer,
2383 VulkanSampler *sampler)
2384{
2385 TRACK_RESOURCE(
2386 sampler,
2387 VulkanSampler *,
2388 usedSamplers,
2389 usedSamplerCount,
2390 usedSamplerCapacity)
2391}
2392
2393static void VULKAN_INTERNAL_TrackGraphicsPipeline(
2394 VulkanCommandBuffer *commandBuffer,
2395 VulkanGraphicsPipeline *graphicsPipeline)
2396{
2397 TRACK_RESOURCE(
2398 graphicsPipeline,
2399 VulkanGraphicsPipeline *,
2400 usedGraphicsPipelines,
2401 usedGraphicsPipelineCount,
2402 usedGraphicsPipelineCapacity)
2403}
2404
2405static void VULKAN_INTERNAL_TrackComputePipeline(
2406 VulkanCommandBuffer *commandBuffer,
2407 VulkanComputePipeline *computePipeline)
2408{
2409 TRACK_RESOURCE(
2410 computePipeline,
2411 VulkanComputePipeline *,
2412 usedComputePipelines,
2413 usedComputePipelineCount,
2414 usedComputePipelineCapacity)
2415}
2416
2417static void VULKAN_INTERNAL_TrackFramebuffer(
2418 VulkanCommandBuffer *commandBuffer,
2419 VulkanFramebuffer *framebuffer)
2420{
2421 TRACK_RESOURCE(
2422 framebuffer,
2423 VulkanFramebuffer *,
2424 usedFramebuffers,
2425 usedFramebufferCount,
2426 usedFramebufferCapacity);
2427}
2428
2429static void VULKAN_INTERNAL_TrackUniformBuffer(
2430 VulkanCommandBuffer *commandBuffer,
2431 VulkanUniformBuffer *uniformBuffer)
2432{
2433 for (Sint32 i = commandBuffer->usedUniformBufferCount - 1; i >= 0; i -= 1) {
2434 if (commandBuffer->usedUniformBuffers[i] == uniformBuffer) {
2435 return;
2436 }
2437 }
2438
2439 if (commandBuffer->usedUniformBufferCount == commandBuffer->usedUniformBufferCapacity) {
2440 commandBuffer->usedUniformBufferCapacity += 1;
2441 commandBuffer->usedUniformBuffers = SDL_realloc(
2442 commandBuffer->usedUniformBuffers,
2443 commandBuffer->usedUniformBufferCapacity * sizeof(VulkanUniformBuffer *));
2444 }
2445 commandBuffer->usedUniformBuffers[commandBuffer->usedUniformBufferCount] = uniformBuffer;
2446 commandBuffer->usedUniformBufferCount += 1;
2447
2448 VULKAN_INTERNAL_TrackBuffer(
2449 commandBuffer,
2450 uniformBuffer->buffer);
2451}
2452
2453#undef TRACK_RESOURCE
2454
2455// Memory Barriers
2456
2457/*
2458 * In Vulkan, we must manually synchronize operations that write to resources on the GPU
2459 * so that read-after-write, write-after-read, and write-after-write hazards do not occur.
2460 * Additionally, textures are required to be in specific layouts for specific use cases.
2461 * Both of these tasks are accomplished with vkCmdPipelineBarrier.
2462 *
2463 * To insert the correct barriers, we keep track of "usage modes" for buffers and textures.
2464 * These indicate the current usage of that resource on the command buffer.
2465 * The transition from one usage mode to another indicates how the barrier should be constructed.
2466 *
2467 * Pipeline barriers cannot be inserted during a render pass, but they can be inserted
2468 * during a compute or copy pass.
2469 *
2470 * This means that the "default" usage mode of any given resource should be that it should be
2471 * ready for a graphics-read operation, because we cannot barrier during a render pass.
2472 * In the case where a resource is only used in compute, its default usage mode can be compute-read.
2473 * This strategy allows us to avoid expensive record keeping of command buffer/resource usage mode pairs,
2474 * and it fully covers synchronization between all combinations of stages.
2475 *
2476 * In Upload and Copy functions, we transition the resource immediately before and after the copy command.
2477 *
2478 * When binding a resource for compute, we transition when the Bind functions are called.
2479 * If a bind slot containing a resource is overwritten, we transition the resource in that slot back to its default.
2480 * When EndComputePass is called we transition all bound resources back to their default state.
2481 *
2482 * When binding a texture as a render pass attachment, we transition the resource on BeginRenderPass
2483 * and transition it back to its default on EndRenderPass.
2484 *
2485 * This strategy imposes certain limitations on resource usage flags.
2486 * For example, a texture cannot have both the SAMPLER and GRAPHICS_STORAGE usage flags,
2487 * because then it is impossible for the backend to infer which default usage mode the texture should use.
2488 *
2489 * Sync hazards can be detected by setting VK_KHRONOS_VALIDATION_VALIDATE_SYNC=1 when using validation layers.
2490 */
2491
2492static void VULKAN_INTERNAL_BufferMemoryBarrier(
2493 VulkanRenderer *renderer,
2494 VulkanCommandBuffer *commandBuffer,
2495 VulkanBufferUsageMode sourceUsageMode,
2496 VulkanBufferUsageMode destinationUsageMode,
2497 VulkanBuffer *buffer)
2498{
2499 VkPipelineStageFlags srcStages = 0;
2500 VkPipelineStageFlags dstStages = 0;
2501 VkBufferMemoryBarrier memoryBarrier;
2502
2503 memoryBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
2504 memoryBarrier.pNext = NULL;
2505 memoryBarrier.srcAccessMask = 0;
2506 memoryBarrier.dstAccessMask = 0;
2507 memoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
2508 memoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
2509 memoryBarrier.buffer = buffer->buffer;
2510 memoryBarrier.offset = 0;
2511 memoryBarrier.size = buffer->size;
2512
2513 if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE) {
2514 srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
2515 memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
2516 } else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION) {
2517 srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
2518 memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
2519 } else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_VERTEX_READ) {
2520 srcStages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
2521 memoryBarrier.srcAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
2522 } else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_INDEX_READ) {
2523 srcStages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
2524 memoryBarrier.srcAccessMask = VK_ACCESS_INDEX_READ_BIT;
2525 } else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_INDIRECT) {
2526 srcStages = VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
2527 memoryBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
2528 } else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_GRAPHICS_STORAGE_READ) {
2529 srcStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
2530 memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
2531 } else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ) {
2532 srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
2533 memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
2534 } else if (sourceUsageMode == VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE) {
2535 srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
2536 memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
2537 } else {
2538 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized buffer source barrier type!");
2539 return;
2540 }
2541
2542 if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE) {
2543 dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
2544 memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
2545 } else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION) {
2546 dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
2547 memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
2548 } else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_VERTEX_READ) {
2549 dstStages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
2550 memoryBarrier.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
2551 } else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_INDEX_READ) {
2552 dstStages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
2553 memoryBarrier.dstAccessMask = VK_ACCESS_INDEX_READ_BIT;
2554 } else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_INDIRECT) {
2555 dstStages = VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
2556 memoryBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
2557 } else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_GRAPHICS_STORAGE_READ) {
2558 dstStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
2559 memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
2560 } else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ) {
2561 dstStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
2562 memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
2563 } else if (destinationUsageMode == VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE) {
2564 dstStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
2565 memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
2566 } else {
2567 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized buffer destination barrier type!");
2568 return;
2569 }
2570
2571 renderer->vkCmdPipelineBarrier(
2572 commandBuffer->commandBuffer,
2573 srcStages,
2574 dstStages,
2575 0,
2576 0,
2577 NULL,
2578 1,
2579 &memoryBarrier,
2580 0,
2581 NULL);
2582
2583 buffer->transitioned = true;
2584}
2585
2586static void VULKAN_INTERNAL_TextureSubresourceMemoryBarrier(
2587 VulkanRenderer *renderer,
2588 VulkanCommandBuffer *commandBuffer,
2589 VulkanTextureUsageMode sourceUsageMode,
2590 VulkanTextureUsageMode destinationUsageMode,
2591 VulkanTextureSubresource *textureSubresource)
2592{
2593 VkPipelineStageFlags srcStages = 0;
2594 VkPipelineStageFlags dstStages = 0;
2595 VkImageMemoryBarrier memoryBarrier;
2596
2597 memoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
2598 memoryBarrier.pNext = NULL;
2599 memoryBarrier.srcAccessMask = 0;
2600 memoryBarrier.dstAccessMask = 0;
2601 memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
2602 memoryBarrier.newLayout = VK_IMAGE_LAYOUT_UNDEFINED;
2603 memoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
2604 memoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
2605 memoryBarrier.image = textureSubresource->parent->image;
2606 memoryBarrier.subresourceRange.aspectMask = textureSubresource->parent->aspectFlags;
2607 memoryBarrier.subresourceRange.baseArrayLayer = textureSubresource->layer;
2608 memoryBarrier.subresourceRange.layerCount = 1;
2609 memoryBarrier.subresourceRange.baseMipLevel = textureSubresource->level;
2610 memoryBarrier.subresourceRange.levelCount = 1;
2611
2612 if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_UNINITIALIZED) {
2613 srcStages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
2614 memoryBarrier.srcAccessMask = 0;
2615 memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
2616 } else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE) {
2617 srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
2618 memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
2619 memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
2620 } else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION) {
2621 srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
2622 memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
2623 memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
2624 } else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_SAMPLER) {
2625 srcStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
2626 memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
2627 memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
2628 } else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_GRAPHICS_STORAGE_READ) {
2629 srcStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
2630 memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
2631 memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
2632 } else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ) {
2633 srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
2634 memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
2635 memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
2636 } else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE) {
2637 srcStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
2638 memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
2639 memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
2640 } else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT) {
2641 srcStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
2642 memoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
2643 memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
2644 } else if (sourceUsageMode == VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT) {
2645 srcStages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
2646 memoryBarrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
2647 memoryBarrier.oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
2648 } else {
2649 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized texture source barrier type!");
2650 return;
2651 }
2652
2653 if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE) {
2654 dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
2655 memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
2656 memoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
2657 } else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION) {
2658 dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
2659 memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
2660 memoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
2661 } else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_SAMPLER) {
2662 dstStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
2663 memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
2664 memoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
2665 } else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_GRAPHICS_STORAGE_READ) {
2666 dstStages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
2667 memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
2668 memoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
2669 } else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ) {
2670 dstStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
2671 memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
2672 memoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
2673 } else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE) {
2674 dstStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
2675 memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
2676 memoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
2677 } else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT) {
2678 dstStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
2679 memoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
2680 memoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
2681 } else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT) {
2682 dstStages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
2683 memoryBarrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
2684 memoryBarrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
2685 } else if (destinationUsageMode == VULKAN_TEXTURE_USAGE_MODE_PRESENT) {
2686 dstStages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
2687 memoryBarrier.dstAccessMask = 0;
2688 memoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
2689 } else {
2690 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized texture destination barrier type!");
2691 return;
2692 }
2693
2694 renderer->vkCmdPipelineBarrier(
2695 commandBuffer->commandBuffer,
2696 srcStages,
2697 dstStages,
2698 0,
2699 0,
2700 NULL,
2701 0,
2702 NULL,
2703 1,
2704 &memoryBarrier);
2705}
2706
2707static VulkanBufferUsageMode VULKAN_INTERNAL_DefaultBufferUsageMode(
2708 VulkanBuffer *buffer)
2709{
2710 // NOTE: order matters here!
2711
2712 if (buffer->usage & SDL_GPU_BUFFERUSAGE_VERTEX) {
2713 return VULKAN_BUFFER_USAGE_MODE_VERTEX_READ;
2714 } else if (buffer->usage & SDL_GPU_BUFFERUSAGE_INDEX) {
2715 return VULKAN_BUFFER_USAGE_MODE_INDEX_READ;
2716 } else if (buffer->usage & SDL_GPU_BUFFERUSAGE_INDIRECT) {
2717 return VULKAN_BUFFER_USAGE_MODE_INDIRECT;
2718 } else if (buffer->usage & SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ) {
2719 return VULKAN_BUFFER_USAGE_MODE_GRAPHICS_STORAGE_READ;
2720 } else if (buffer->usage & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ) {
2721 return VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ;
2722 } else if (buffer->usage & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE) {
2723 return VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE;
2724 } else {
2725 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Buffer has no default usage mode!");
2726 return VULKAN_BUFFER_USAGE_MODE_VERTEX_READ;
2727 }
2728}
2729
2730static VulkanTextureUsageMode VULKAN_INTERNAL_DefaultTextureUsageMode(
2731 VulkanTexture *texture)
2732{
2733 // NOTE: order matters here!
2734 // NOTE: graphics storage bits and sampler bit are mutually exclusive!
2735
2736 if (texture->usage & SDL_GPU_TEXTUREUSAGE_SAMPLER) {
2737 return VULKAN_TEXTURE_USAGE_MODE_SAMPLER;
2738 } else if (texture->usage & SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ) {
2739 return VULKAN_TEXTURE_USAGE_MODE_GRAPHICS_STORAGE_READ;
2740 } else if (texture->usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
2741 return VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT;
2742 } else if (texture->usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
2743 return VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT;
2744 } else if (texture->usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ) {
2745 return VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ;
2746 } else if (texture->usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) {
2747 return VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE;
2748 } else if (texture->usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE) {
2749 return VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE;
2750 } else {
2751 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Texture has no default usage mode!");
2752 return VULKAN_TEXTURE_USAGE_MODE_SAMPLER;
2753 }
2754}
2755
2756static void VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
2757 VulkanRenderer *renderer,
2758 VulkanCommandBuffer *commandBuffer,
2759 VulkanBufferUsageMode destinationUsageMode,
2760 VulkanBuffer *buffer)
2761{
2762 VULKAN_INTERNAL_BufferMemoryBarrier(
2763 renderer,
2764 commandBuffer,
2765 VULKAN_INTERNAL_DefaultBufferUsageMode(buffer),
2766 destinationUsageMode,
2767 buffer);
2768}
2769
2770static void VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
2771 VulkanRenderer *renderer,
2772 VulkanCommandBuffer *commandBuffer,
2773 VulkanBufferUsageMode sourceUsageMode,
2774 VulkanBuffer *buffer)
2775{
2776 VULKAN_INTERNAL_BufferMemoryBarrier(
2777 renderer,
2778 commandBuffer,
2779 sourceUsageMode,
2780 VULKAN_INTERNAL_DefaultBufferUsageMode(buffer),
2781 buffer);
2782}
2783
2784static void VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
2785 VulkanRenderer *renderer,
2786 VulkanCommandBuffer *commandBuffer,
2787 VulkanTextureUsageMode destinationUsageMode,
2788 VulkanTextureSubresource *textureSubresource)
2789{
2790 VULKAN_INTERNAL_TextureSubresourceMemoryBarrier(
2791 renderer,
2792 commandBuffer,
2793 VULKAN_INTERNAL_DefaultTextureUsageMode(textureSubresource->parent),
2794 destinationUsageMode,
2795 textureSubresource);
2796}
2797
2798static void VULKAN_INTERNAL_TextureTransitionFromDefaultUsage(
2799 VulkanRenderer *renderer,
2800 VulkanCommandBuffer *commandBuffer,
2801 VulkanTextureUsageMode destinationUsageMode,
2802 VulkanTexture *texture)
2803{
2804 for (Uint32 i = 0; i < texture->subresourceCount; i += 1) {
2805 VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
2806 renderer,
2807 commandBuffer,
2808 destinationUsageMode,
2809 &texture->subresources[i]);
2810 }
2811}
2812
2813static void VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
2814 VulkanRenderer *renderer,
2815 VulkanCommandBuffer *commandBuffer,
2816 VulkanTextureUsageMode sourceUsageMode,
2817 VulkanTextureSubresource *textureSubresource)
2818{
2819 VULKAN_INTERNAL_TextureSubresourceMemoryBarrier(
2820 renderer,
2821 commandBuffer,
2822 sourceUsageMode,
2823 VULKAN_INTERNAL_DefaultTextureUsageMode(textureSubresource->parent),
2824 textureSubresource);
2825}
2826
2827static void VULKAN_INTERNAL_TextureTransitionToDefaultUsage(
2828 VulkanRenderer *renderer,
2829 VulkanCommandBuffer *commandBuffer,
2830 VulkanTextureUsageMode sourceUsageMode,
2831 VulkanTexture *texture)
2832{
2833 // FIXME: could optimize this barrier
2834 for (Uint32 i = 0; i < texture->subresourceCount; i += 1) {
2835 VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
2836 renderer,
2837 commandBuffer,
2838 sourceUsageMode,
2839 &texture->subresources[i]);
2840 }
2841}
2842
2843// Resource Disposal
2844
2845static void VULKAN_INTERNAL_ReleaseFramebuffer(
2846 VulkanRenderer *renderer,
2847 VulkanFramebuffer *framebuffer)
2848{
2849 SDL_LockMutex(renderer->disposeLock);
2850
2851 EXPAND_ARRAY_IF_NEEDED(
2852 renderer->framebuffersToDestroy,
2853 VulkanFramebuffer *,
2854 renderer->framebuffersToDestroyCount + 1,
2855 renderer->framebuffersToDestroyCapacity,
2856 renderer->framebuffersToDestroyCapacity * 2);
2857
2858 renderer->framebuffersToDestroy[renderer->framebuffersToDestroyCount] = framebuffer;
2859 renderer->framebuffersToDestroyCount += 1;
2860
2861 SDL_UnlockMutex(renderer->disposeLock);
2862}
2863
2864static void VULKAN_INTERNAL_DestroyFramebuffer(
2865 VulkanRenderer *renderer,
2866 VulkanFramebuffer *framebuffer)
2867{
2868 renderer->vkDestroyFramebuffer(
2869 renderer->logicalDevice,
2870 framebuffer->framebuffer,
2871 NULL);
2872
2873 SDL_free(framebuffer);
2874}
2875
2876typedef struct CheckOneFramebufferForRemovalData
2877{
2878 Uint32 keysToRemoveCapacity;
2879 Uint32 keysToRemoveCount;
2880 FramebufferHashTableKey **keysToRemove;
2881 VkImageView view;
2882} CheckOneFramebufferForRemovalData;
2883
2884static bool SDLCALL CheckOneFramebufferForRemoval(void *userdata, const SDL_HashTable *table, const void *vkey, const void *vvalue)
2885{
2886 CheckOneFramebufferForRemovalData *data = (CheckOneFramebufferForRemovalData *) userdata;
2887 FramebufferHashTableKey *key = (FramebufferHashTableKey *) vkey;
2888 VkImageView view = data->view;
2889 bool remove = false;
2890
2891 for (Uint32 i = 0; i < key->numColorTargets; i += 1) {
2892 if (key->colorAttachmentViews[i] == view) {
2893 remove = true;
2894 }
2895 }
2896 for (Uint32 i = 0; i < key->numResolveAttachments; i += 1) {
2897 if (key->resolveAttachmentViews[i] == view) {
2898 remove = true;
2899 }
2900 }
2901 if (key->depthStencilAttachmentView == view) {
2902 remove = true;
2903 }
2904
2905 if (remove) {
2906 if (data->keysToRemoveCount == data->keysToRemoveCapacity) {
2907 data->keysToRemoveCapacity *= 2;
2908 void *ptr = SDL_realloc(data->keysToRemove, data->keysToRemoveCapacity * sizeof(FramebufferHashTableKey *));
2909 if (!ptr) {
2910 return false; // ugh, stop iterating. We're in trouble.
2911 }
2912 data->keysToRemove = (FramebufferHashTableKey **) ptr;
2913 }
2914 data->keysToRemove[data->keysToRemoveCount] = key;
2915 data->keysToRemoveCount++;
2916 }
2917
2918 return true; // keep iterating.
2919}
2920
2921static void VULKAN_INTERNAL_RemoveFramebuffersContainingView(
2922 VulkanRenderer *renderer,
2923 VkImageView view)
2924{
2925 // Can't remove while iterating!
2926
2927 CheckOneFramebufferForRemovalData data = { 8, 0, NULL, view };
2928 data.keysToRemove = (FramebufferHashTableKey **) SDL_malloc(data.keysToRemoveCapacity * sizeof(FramebufferHashTableKey *));
2929 if (!data.keysToRemove) {
2930 return; // uhoh.
2931 }
2932
2933 SDL_LockMutex(renderer->framebufferFetchLock);
2934
2935 SDL_IterateHashTable(renderer->framebufferHashTable, CheckOneFramebufferForRemoval, &data);
2936
2937 for (Uint32 i = 0; i < data.keysToRemoveCount; i += 1) {
2938 SDL_RemoveFromHashTable(renderer->framebufferHashTable, (void *)data.keysToRemove[i]);
2939 }
2940
2941 SDL_UnlockMutex(renderer->framebufferFetchLock);
2942
2943 SDL_free(data.keysToRemove);
2944}
2945
2946static void VULKAN_INTERNAL_DestroyTexture(
2947 VulkanRenderer *renderer,
2948 VulkanTexture *texture)
2949{
2950 // Clean up subresources
2951 for (Uint32 subresourceIndex = 0; subresourceIndex < texture->subresourceCount; subresourceIndex += 1) {
2952 if (texture->subresources[subresourceIndex].renderTargetViews != NULL) {
2953 for (Uint32 depthIndex = 0; depthIndex < texture->depth; depthIndex += 1) {
2954 VULKAN_INTERNAL_RemoveFramebuffersContainingView(
2955 renderer,
2956 texture->subresources[subresourceIndex].renderTargetViews[depthIndex]);
2957 }
2958
2959 for (Uint32 depthIndex = 0; depthIndex < texture->depth; depthIndex += 1) {
2960 renderer->vkDestroyImageView(
2961 renderer->logicalDevice,
2962 texture->subresources[subresourceIndex].renderTargetViews[depthIndex],
2963 NULL);
2964 }
2965 SDL_free(texture->subresources[subresourceIndex].renderTargetViews);
2966 }
2967
2968 if (texture->subresources[subresourceIndex].computeWriteView != VK_NULL_HANDLE) {
2969 renderer->vkDestroyImageView(
2970 renderer->logicalDevice,
2971 texture->subresources[subresourceIndex].computeWriteView,
2972 NULL);
2973 }
2974
2975 if (texture->subresources[subresourceIndex].depthStencilView != VK_NULL_HANDLE) {
2976 VULKAN_INTERNAL_RemoveFramebuffersContainingView(
2977 renderer,
2978 texture->subresources[subresourceIndex].depthStencilView);
2979 renderer->vkDestroyImageView(
2980 renderer->logicalDevice,
2981 texture->subresources[subresourceIndex].depthStencilView,
2982 NULL);
2983 }
2984 }
2985
2986 SDL_free(texture->subresources);
2987
2988 if (texture->fullView) {
2989 renderer->vkDestroyImageView(
2990 renderer->logicalDevice,
2991 texture->fullView,
2992 NULL);
2993 }
2994
2995 if (texture->image) {
2996 renderer->vkDestroyImage(
2997 renderer->logicalDevice,
2998 texture->image,
2999 NULL);
3000 }
3001
3002 if (texture->usedRegion) {
3003 VULKAN_INTERNAL_RemoveMemoryUsedRegion(
3004 renderer,
3005 texture->usedRegion);
3006 }
3007
3008 SDL_free(texture);
3009}
3010
3011static void VULKAN_INTERNAL_DestroyBuffer(
3012 VulkanRenderer *renderer,
3013 VulkanBuffer *buffer)
3014{
3015 renderer->vkDestroyBuffer(
3016 renderer->logicalDevice,
3017 buffer->buffer,
3018 NULL);
3019
3020 VULKAN_INTERNAL_RemoveMemoryUsedRegion(
3021 renderer,
3022 buffer->usedRegion);
3023
3024 SDL_free(buffer);
3025}
3026
3027static void VULKAN_INTERNAL_DestroyCommandPool(
3028 VulkanRenderer *renderer,
3029 VulkanCommandPool *commandPool)
3030{
3031 Uint32 i;
3032 VulkanCommandBuffer *commandBuffer;
3033
3034 renderer->vkDestroyCommandPool(
3035 renderer->logicalDevice,
3036 commandPool->commandPool,
3037 NULL);
3038
3039 for (i = 0; i < commandPool->inactiveCommandBufferCount; i += 1) {
3040 commandBuffer = commandPool->inactiveCommandBuffers[i];
3041
3042 SDL_free(commandBuffer->presentDatas);
3043 SDL_free(commandBuffer->waitSemaphores);
3044 SDL_free(commandBuffer->signalSemaphores);
3045 SDL_free(commandBuffer->usedBuffers);
3046 SDL_free(commandBuffer->usedTextures);
3047 SDL_free(commandBuffer->usedSamplers);
3048 SDL_free(commandBuffer->usedGraphicsPipelines);
3049 SDL_free(commandBuffer->usedComputePipelines);
3050 SDL_free(commandBuffer->usedFramebuffers);
3051 SDL_free(commandBuffer->usedUniformBuffers);
3052
3053 SDL_free(commandBuffer);
3054 }
3055
3056 SDL_free(commandPool->inactiveCommandBuffers);
3057 SDL_free(commandPool);
3058}
3059
3060static void VULKAN_INTERNAL_DestroyDescriptorSetLayout(
3061 VulkanRenderer *renderer,
3062 DescriptorSetLayout *layout)
3063{
3064 if (layout == NULL) {
3065 return;
3066 }
3067
3068 if (layout->descriptorSetLayout != VK_NULL_HANDLE) {
3069 renderer->vkDestroyDescriptorSetLayout(
3070 renderer->logicalDevice,
3071 layout->descriptorSetLayout,
3072 NULL);
3073 }
3074
3075 SDL_free(layout);
3076}
3077
3078static void VULKAN_INTERNAL_DestroyGraphicsPipeline(
3079 VulkanRenderer *renderer,
3080 VulkanGraphicsPipeline *graphicsPipeline)
3081{
3082 renderer->vkDestroyPipeline(
3083 renderer->logicalDevice,
3084 graphicsPipeline->pipeline,
3085 NULL);
3086
3087 (void)SDL_AtomicDecRef(&graphicsPipeline->vertexShader->referenceCount);
3088 (void)SDL_AtomicDecRef(&graphicsPipeline->fragmentShader->referenceCount);
3089
3090 SDL_free(graphicsPipeline);
3091}
3092
3093static void VULKAN_INTERNAL_DestroyComputePipeline(
3094 VulkanRenderer *renderer,
3095 VulkanComputePipeline *computePipeline)
3096{
3097 if (computePipeline->pipeline != VK_NULL_HANDLE) {
3098 renderer->vkDestroyPipeline(
3099 renderer->logicalDevice,
3100 computePipeline->pipeline,
3101 NULL);
3102 }
3103
3104 if (computePipeline->shaderModule != VK_NULL_HANDLE) {
3105 renderer->vkDestroyShaderModule(
3106 renderer->logicalDevice,
3107 computePipeline->shaderModule,
3108 NULL);
3109 }
3110
3111 SDL_free(computePipeline);
3112}
3113
3114static void VULKAN_INTERNAL_DestroyShader(
3115 VulkanRenderer *renderer,
3116 VulkanShader *vulkanShader)
3117{
3118 renderer->vkDestroyShaderModule(
3119 renderer->logicalDevice,
3120 vulkanShader->shaderModule,
3121 NULL);
3122
3123 SDL_free(vulkanShader->entrypointName);
3124 SDL_free(vulkanShader);
3125}
3126
3127static void VULKAN_INTERNAL_DestroySampler(
3128 VulkanRenderer *renderer,
3129 VulkanSampler *vulkanSampler)
3130{
3131 renderer->vkDestroySampler(
3132 renderer->logicalDevice,
3133 vulkanSampler->sampler,
3134 NULL);
3135
3136 SDL_free(vulkanSampler);
3137}
3138
3139static void VULKAN_INTERNAL_DestroySwapchain(
3140 VulkanRenderer *renderer,
3141 WindowData *windowData)
3142{
3143 Uint32 i;
3144
3145 if (windowData == NULL) {
3146 return;
3147 }
3148
3149 for (i = 0; i < windowData->imageCount; i += 1) {
3150 VULKAN_INTERNAL_RemoveFramebuffersContainingView(
3151 renderer,
3152 windowData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0]);
3153 renderer->vkDestroyImageView(
3154 renderer->logicalDevice,
3155 windowData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0],
3156 NULL);
3157 SDL_free(windowData->textureContainers[i].activeTexture->subresources[0].renderTargetViews);
3158 SDL_free(windowData->textureContainers[i].activeTexture->subresources);
3159 SDL_free(windowData->textureContainers[i].activeTexture);
3160 }
3161 windowData->imageCount = 0;
3162
3163 SDL_free(windowData->textureContainers);
3164 windowData->textureContainers = NULL;
3165
3166 if (windowData->swapchain) {
3167 renderer->vkDestroySwapchainKHR(
3168 renderer->logicalDevice,
3169 windowData->swapchain,
3170 NULL);
3171 windowData->swapchain = VK_NULL_HANDLE;
3172 }
3173
3174 if (windowData->surface) {
3175 renderer->vkDestroySurfaceKHR(
3176 renderer->instance,
3177 windowData->surface,
3178 NULL);
3179 windowData->surface = VK_NULL_HANDLE;
3180 }
3181
3182 for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
3183 if (windowData->imageAvailableSemaphore[i]) {
3184 renderer->vkDestroySemaphore(
3185 renderer->logicalDevice,
3186 windowData->imageAvailableSemaphore[i],
3187 NULL);
3188 windowData->imageAvailableSemaphore[i] = VK_NULL_HANDLE;
3189 }
3190
3191 if (windowData->renderFinishedSemaphore[i]) {
3192 renderer->vkDestroySemaphore(
3193 renderer->logicalDevice,
3194 windowData->renderFinishedSemaphore[i],
3195 NULL);
3196 windowData->renderFinishedSemaphore[i] = VK_NULL_HANDLE;
3197 }
3198 }
3199}
3200
3201static void VULKAN_INTERNAL_DestroyGraphicsPipelineResourceLayout(
3202 VulkanRenderer *renderer,
3203 VulkanGraphicsPipelineResourceLayout *resourceLayout)
3204{
3205 if (resourceLayout->pipelineLayout != VK_NULL_HANDLE) {
3206 renderer->vkDestroyPipelineLayout(
3207 renderer->logicalDevice,
3208 resourceLayout->pipelineLayout,
3209 NULL);
3210 }
3211
3212 SDL_free(resourceLayout);
3213}
3214
3215static void VULKAN_INTERNAL_DestroyComputePipelineResourceLayout(
3216 VulkanRenderer *renderer,
3217 VulkanComputePipelineResourceLayout *resourceLayout)
3218{
3219 if (resourceLayout->pipelineLayout != VK_NULL_HANDLE) {
3220 renderer->vkDestroyPipelineLayout(
3221 renderer->logicalDevice,
3222 resourceLayout->pipelineLayout,
3223 NULL);
3224 }
3225
3226 SDL_free(resourceLayout);
3227}
3228
3229static void VULKAN_INTERNAL_DestroyDescriptorSetCache(
3230 VulkanRenderer *renderer,
3231 DescriptorSetCache *descriptorSetCache)
3232{
3233 for (Uint32 i = 0; i < descriptorSetCache->poolCount; i += 1) {
3234 for (Uint32 j = 0; j < descriptorSetCache->pools[i].poolCount; j += 1) {
3235 renderer->vkDestroyDescriptorPool(
3236 renderer->logicalDevice,
3237 descriptorSetCache->pools[i].descriptorPools[j],
3238 NULL);
3239 }
3240 SDL_free(descriptorSetCache->pools[i].descriptorSets);
3241 SDL_free(descriptorSetCache->pools[i].descriptorPools);
3242 }
3243 SDL_free(descriptorSetCache->pools);
3244 SDL_free(descriptorSetCache);
3245}
3246
3247// Hashtable functions
3248
3249static Uint32 SDLCALL VULKAN_INTERNAL_GraphicsPipelineResourceLayoutHashFunction(void *userdata, const void *key)
3250{
3251 GraphicsPipelineResourceLayoutHashTableKey *hashTableKey = (GraphicsPipelineResourceLayoutHashTableKey *)key;
3252 /* The algorithm for this hashing function
3253 * is taken from Josh Bloch's "Effective Java".
3254 * (https://stackoverflow.com/a/113600/12492383)
3255 */
3256 const Uint32 hashFactor = 31;
3257 Uint32 result = 1;
3258 result = result * hashFactor + hashTableKey->vertexSamplerCount;
3259 result = result * hashFactor + hashTableKey->vertexStorageBufferCount;
3260 result = result * hashFactor + hashTableKey->vertexStorageTextureCount;
3261 result = result * hashFactor + hashTableKey->vertexUniformBufferCount;
3262 result = result * hashFactor + hashTableKey->fragmentSamplerCount;
3263 result = result * hashFactor + hashTableKey->fragmentStorageBufferCount;
3264 result = result * hashFactor + hashTableKey->fragmentStorageTextureCount;
3265 result = result * hashFactor + hashTableKey->fragmentUniformBufferCount;
3266 return result;
3267}
3268static bool SDLCALL VULKAN_INTERNAL_GraphicsPipelineResourceLayoutHashKeyMatch(void *userdata, const void *aKey, const void *bKey)
3269{
3270 return SDL_memcmp(aKey, bKey, sizeof(GraphicsPipelineResourceLayoutHashTableKey)) == 0;
3271}
3272static void SDLCALL VULKAN_INTERNAL_GraphicsPipelineResourceLayoutHashDestroy(void *userdata, const void *key, const void *value)
3273{
3274 VulkanRenderer *renderer = (VulkanRenderer *)userdata;
3275 VulkanGraphicsPipelineResourceLayout *resourceLayout = (VulkanGraphicsPipelineResourceLayout *)value;
3276 VULKAN_INTERNAL_DestroyGraphicsPipelineResourceLayout(renderer, resourceLayout);
3277 SDL_free((void*)key);
3278}
3279
3280static Uint32 SDLCALL VULKAN_INTERNAL_ComputePipelineResourceLayoutHashFunction(void *userdata, const void *key)
3281{
3282 ComputePipelineResourceLayoutHashTableKey *hashTableKey = (ComputePipelineResourceLayoutHashTableKey *)key;
3283 /* The algorithm for this hashing function
3284 * is taken from Josh Bloch's "Effective Java".
3285 * (https://stackoverflow.com/a/113600/12492383)
3286 */
3287 const Uint32 hashFactor = 31;
3288 Uint32 result = 1;
3289 result = result * hashFactor + hashTableKey->samplerCount;
3290 result = result * hashFactor + hashTableKey->readonlyStorageTextureCount;
3291 result = result * hashFactor + hashTableKey->readonlyStorageBufferCount;
3292 result = result * hashFactor + hashTableKey->readWriteStorageTextureCount;
3293 result = result * hashFactor + hashTableKey->readWriteStorageBufferCount;
3294 result = result * hashFactor + hashTableKey->uniformBufferCount;
3295 return result;
3296}
3297
3298static bool SDLCALL VULKAN_INTERNAL_ComputePipelineResourceLayoutHashKeyMatch(void *userdata, const void *aKey, const void *bKey)
3299{
3300 return SDL_memcmp(aKey, bKey, sizeof(ComputePipelineResourceLayoutHashTableKey)) == 0;
3301}
3302
3303static void SDLCALL VULKAN_INTERNAL_ComputePipelineResourceLayoutHashDestroy(void *userdata, const void *key, const void *value)
3304{
3305 VulkanRenderer *renderer = (VulkanRenderer *)userdata;
3306 VulkanComputePipelineResourceLayout *resourceLayout = (VulkanComputePipelineResourceLayout *)value;
3307 VULKAN_INTERNAL_DestroyComputePipelineResourceLayout(renderer, resourceLayout);
3308 SDL_free((void*)key);
3309}
3310
3311static Uint32 SDLCALL VULKAN_INTERNAL_DescriptorSetLayoutHashFunction(void *userdata, const void *key)
3312{
3313 DescriptorSetLayoutHashTableKey *hashTableKey = (DescriptorSetLayoutHashTableKey *)key;
3314
3315 /* The algorithm for this hashing function
3316 * is taken from Josh Bloch's "Effective Java".
3317 * (https://stackoverflow.com/a/113600/12492383)
3318 */
3319 const Uint32 hashFactor = 31;
3320 Uint32 result = 1;
3321 result = result * hashFactor + hashTableKey->shaderStage;
3322 result = result * hashFactor + hashTableKey->samplerCount;
3323 result = result * hashFactor + hashTableKey->storageTextureCount;
3324 result = result * hashFactor + hashTableKey->storageBufferCount;
3325 result = result * hashFactor + hashTableKey->writeStorageTextureCount;
3326 result = result * hashFactor + hashTableKey->writeStorageBufferCount;
3327 result = result * hashFactor + hashTableKey->uniformBufferCount;
3328 return result;
3329}
3330
3331static bool SDLCALL VULKAN_INTERNAL_DescriptorSetLayoutHashKeyMatch(void *userdata, const void *aKey, const void *bKey)
3332{
3333 return SDL_memcmp(aKey, bKey, sizeof(DescriptorSetLayoutHashTableKey)) == 0;
3334}
3335
3336static void SDLCALL VULKAN_INTERNAL_DescriptorSetLayoutHashDestroy(void *userdata, const void *key, const void *value)
3337{
3338 VulkanRenderer *renderer = (VulkanRenderer *)userdata;
3339 DescriptorSetLayout *layout = (DescriptorSetLayout *)value;
3340 VULKAN_INTERNAL_DestroyDescriptorSetLayout(renderer, layout);
3341 SDL_free((void*)key);
3342}
3343
3344static Uint32 SDLCALL VULKAN_INTERNAL_CommandPoolHashFunction(void *userdata, const void *key)
3345{
3346 return (Uint32)((CommandPoolHashTableKey *)key)->threadID;
3347}
3348
3349static bool SDLCALL VULKAN_INTERNAL_CommandPoolHashKeyMatch(void *userdata, const void *aKey, const void *bKey)
3350{
3351 CommandPoolHashTableKey *a = (CommandPoolHashTableKey *)aKey;
3352 CommandPoolHashTableKey *b = (CommandPoolHashTableKey *)bKey;
3353 return a->threadID == b->threadID;
3354}
3355
3356static void SDLCALL VULKAN_INTERNAL_CommandPoolHashDestroy(void *userdata, const void *key, const void *value)
3357{
3358 VulkanRenderer *renderer = (VulkanRenderer *)userdata;
3359 VulkanCommandPool *pool = (VulkanCommandPool *)value;
3360 VULKAN_INTERNAL_DestroyCommandPool(renderer, pool);
3361 SDL_free((void *)key);
3362}
3363
3364static Uint32 SDLCALL VULKAN_INTERNAL_RenderPassHashFunction(void *userdata, const void *key)
3365{
3366 RenderPassHashTableKey *hashTableKey = (RenderPassHashTableKey *)key;
3367
3368 /* The algorithm for this hashing function
3369 * is taken from Josh Bloch's "Effective Java".
3370 * (https://stackoverflow.com/a/113600/12492383)
3371 */
3372 const Uint32 hashFactor = 31;
3373 Uint32 result = 1;
3374
3375 for (Uint32 i = 0; i < hashTableKey->numColorTargets; i += 1) {
3376 result = result * hashFactor + hashTableKey->colorTargetDescriptions[i].loadOp;
3377 result = result * hashFactor + hashTableKey->colorTargetDescriptions[i].storeOp;
3378 result = result * hashFactor + hashTableKey->colorTargetDescriptions[i].format;
3379 }
3380
3381 for (Uint32 i = 0; i < hashTableKey->numResolveTargets; i += 1) {
3382 result = result * hashFactor + hashTableKey->resolveTargetFormats[i];
3383 }
3384
3385 result = result * hashFactor + hashTableKey->depthStencilTargetDescription.loadOp;
3386 result = result * hashFactor + hashTableKey->depthStencilTargetDescription.storeOp;
3387 result = result * hashFactor + hashTableKey->depthStencilTargetDescription.stencilLoadOp;
3388 result = result * hashFactor + hashTableKey->depthStencilTargetDescription.stencilStoreOp;
3389 result = result * hashFactor + hashTableKey->depthStencilTargetDescription.format;
3390
3391 result = result * hashFactor + hashTableKey->sampleCount;
3392
3393 return result;
3394}
3395
3396static bool SDLCALL VULKAN_INTERNAL_RenderPassHashKeyMatch(void *userdata, const void *aKey, const void *bKey)
3397{
3398 RenderPassHashTableKey *a = (RenderPassHashTableKey *)aKey;
3399 RenderPassHashTableKey *b = (RenderPassHashTableKey *)bKey;
3400
3401 if (a->numColorTargets != b->numColorTargets) {
3402 return 0;
3403 }
3404
3405 if (a->numResolveTargets != b->numResolveTargets) {
3406 return 0;
3407 }
3408
3409 if (a->sampleCount != b->sampleCount) {
3410 return 0;
3411 }
3412
3413 for (Uint32 i = 0; i < a->numColorTargets; i += 1) {
3414 if (a->colorTargetDescriptions[i].format != b->colorTargetDescriptions[i].format) {
3415 return 0;
3416 }
3417
3418 if (a->colorTargetDescriptions[i].loadOp != b->colorTargetDescriptions[i].loadOp) {
3419 return 0;
3420 }
3421
3422 if (a->colorTargetDescriptions[i].storeOp != b->colorTargetDescriptions[i].storeOp) {
3423 return 0;
3424 }
3425 }
3426
3427 for (Uint32 i = 0; i < a->numResolveTargets; i += 1) {
3428 if (a->resolveTargetFormats[i] != b->resolveTargetFormats[i]) {
3429 return 0;
3430 }
3431 }
3432
3433 if (a->depthStencilTargetDescription.format != b->depthStencilTargetDescription.format) {
3434 return 0;
3435 }
3436
3437 if (a->depthStencilTargetDescription.loadOp != b->depthStencilTargetDescription.loadOp) {
3438 return 0;
3439 }
3440
3441 if (a->depthStencilTargetDescription.storeOp != b->depthStencilTargetDescription.storeOp) {
3442 return 0;
3443 }
3444
3445 if (a->depthStencilTargetDescription.stencilLoadOp != b->depthStencilTargetDescription.stencilLoadOp) {
3446 return 0;
3447 }
3448
3449 if (a->depthStencilTargetDescription.stencilStoreOp != b->depthStencilTargetDescription.stencilStoreOp) {
3450 return 0;
3451 }
3452
3453 return 1;
3454}
3455
3456static void SDLCALL VULKAN_INTERNAL_RenderPassHashDestroy(void *userdata, const void *key, const void *value)
3457{
3458 VulkanRenderer *renderer = (VulkanRenderer *)userdata;
3459 VulkanRenderPassHashTableValue *renderPassWrapper = (VulkanRenderPassHashTableValue *)value;
3460 renderer->vkDestroyRenderPass(
3461 renderer->logicalDevice,
3462 renderPassWrapper->handle,
3463 NULL);
3464 SDL_free(renderPassWrapper);
3465 SDL_free((void *)key);
3466}
3467
3468static Uint32 SDLCALL VULKAN_INTERNAL_FramebufferHashFunction(void *userdata, const void *key)
3469{
3470 FramebufferHashTableKey *hashTableKey = (FramebufferHashTableKey *)key;
3471
3472 /* The algorithm for this hashing function
3473 * is taken from Josh Bloch's "Effective Java".
3474 * (https://stackoverflow.com/a/113600/12492383)
3475 */
3476 const Uint32 hashFactor = 31;
3477 Uint32 result = 1;
3478
3479 for (Uint32 i = 0; i < hashTableKey->numColorTargets; i += 1) {
3480 result = result * hashFactor + (Uint32)(uintptr_t)hashTableKey->colorAttachmentViews[i];
3481 }
3482 for (Uint32 i = 0; i < hashTableKey->numResolveAttachments; i += 1) {
3483 result = result * hashFactor + (Uint32)(uintptr_t)hashTableKey->resolveAttachmentViews[i];
3484 }
3485
3486 result = result * hashFactor + (Uint32)(uintptr_t)hashTableKey->depthStencilAttachmentView;
3487 result = result * hashFactor + hashTableKey->width;
3488 result = result * hashFactor + hashTableKey->height;
3489
3490 return result;
3491}
3492
3493static bool SDLCALL VULKAN_INTERNAL_FramebufferHashKeyMatch(void *userdata, const void *aKey, const void *bKey)
3494{
3495 FramebufferHashTableKey *a = (FramebufferHashTableKey *)aKey;
3496 FramebufferHashTableKey *b = (FramebufferHashTableKey *)bKey;
3497
3498 if (a->numColorTargets != b->numColorTargets) {
3499 return 0;
3500 }
3501
3502 if (a->numResolveAttachments != b->numResolveAttachments) {
3503 return 0;
3504 }
3505
3506 for (Uint32 i = 0; i < a->numColorTargets; i += 1) {
3507 if (a->colorAttachmentViews[i] != b->colorAttachmentViews[i]) {
3508 return 0;
3509 }
3510 }
3511
3512 for (Uint32 i = 0; i < a->numResolveAttachments; i += 1) {
3513 if (a->resolveAttachmentViews[i] != b->resolveAttachmentViews[i]) {
3514 return 0;
3515 }
3516 }
3517
3518 if (a->depthStencilAttachmentView != b->depthStencilAttachmentView) {
3519 return 0;
3520 }
3521
3522 if (a->width != b->width) {
3523 return 0;
3524 }
3525
3526 if (a->height != b->height) {
3527 return 0;
3528 }
3529
3530 return 1;
3531}
3532
3533static void SDLCALL VULKAN_INTERNAL_FramebufferHashDestroy(void *userdata, const void *key, const void *value)
3534{
3535 VulkanRenderer *renderer = (VulkanRenderer *)userdata;
3536 VulkanFramebuffer *framebuffer = (VulkanFramebuffer *)value;
3537 VULKAN_INTERNAL_ReleaseFramebuffer(renderer, framebuffer);
3538 SDL_free((void *)key);
3539}
3540
3541// Descriptor pools
3542
3543static bool VULKAN_INTERNAL_AllocateDescriptorSets(
3544 VulkanRenderer *renderer,
3545 VkDescriptorPool descriptorPool,
3546 VkDescriptorSetLayout descriptorSetLayout,
3547 Uint32 descriptorSetCount,
3548 VkDescriptorSet *descriptorSetArray)
3549{
3550 VkDescriptorSetAllocateInfo descriptorSetAllocateInfo;
3551 VkDescriptorSetLayout *descriptorSetLayouts = SDL_stack_alloc(VkDescriptorSetLayout, descriptorSetCount);
3552 VkResult vulkanResult;
3553 Uint32 i;
3554
3555 for (i = 0; i < descriptorSetCount; i += 1) {
3556 descriptorSetLayouts[i] = descriptorSetLayout;
3557 }
3558
3559 descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
3560 descriptorSetAllocateInfo.pNext = NULL;
3561 descriptorSetAllocateInfo.descriptorPool = descriptorPool;
3562 descriptorSetAllocateInfo.descriptorSetCount = descriptorSetCount;
3563 descriptorSetAllocateInfo.pSetLayouts = descriptorSetLayouts;
3564
3565 vulkanResult = renderer->vkAllocateDescriptorSets(
3566 renderer->logicalDevice,
3567 &descriptorSetAllocateInfo,
3568 descriptorSetArray);
3569
3570 SDL_stack_free(descriptorSetLayouts);
3571
3572 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkAllocateDescriptorSets, false);
3573
3574 return true;
3575}
3576
3577static bool VULKAN_INTERNAL_AllocateDescriptorsFromPool(
3578 VulkanRenderer *renderer,
3579 DescriptorSetLayout *descriptorSetLayout,
3580 DescriptorSetPool *descriptorSetPool)
3581{
3582 VkDescriptorPoolSize descriptorPoolSizes[
3583 MAX_TEXTURE_SAMPLERS_PER_STAGE +
3584 MAX_STORAGE_TEXTURES_PER_STAGE +
3585 MAX_STORAGE_BUFFERS_PER_STAGE +
3586 MAX_COMPUTE_WRITE_TEXTURES +
3587 MAX_COMPUTE_WRITE_BUFFERS +
3588 MAX_UNIFORM_BUFFERS_PER_STAGE];
3589 VkDescriptorPoolCreateInfo descriptorPoolInfo;
3590 VkDescriptorPool pool;
3591 VkResult vulkanResult;
3592
3593 // Category 1
3594 for (Uint32 i = 0; i < descriptorSetLayout->samplerCount; i += 1) {
3595 descriptorPoolSizes[i].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
3596 descriptorPoolSizes[i].descriptorCount = DESCRIPTOR_POOL_SIZE;
3597 }
3598
3599 for (Uint32 i = descriptorSetLayout->samplerCount; i < descriptorSetLayout->samplerCount + descriptorSetLayout->storageTextureCount; i += 1) {
3600 descriptorPoolSizes[i].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; // Yes, we are declaring the storage image as a sampled image, because shaders are stupid.
3601 descriptorPoolSizes[i].descriptorCount = DESCRIPTOR_POOL_SIZE;
3602 }
3603
3604 for (Uint32 i = descriptorSetLayout->samplerCount + descriptorSetLayout->storageTextureCount; i < descriptorSetLayout->samplerCount + descriptorSetLayout->storageTextureCount + descriptorSetLayout->storageBufferCount; i += 1) {
3605 descriptorPoolSizes[i].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
3606 descriptorPoolSizes[i].descriptorCount = DESCRIPTOR_POOL_SIZE;
3607 }
3608
3609 // Category 2
3610 for (Uint32 i = 0; i < descriptorSetLayout->writeStorageTextureCount; i += 1) {
3611 descriptorPoolSizes[i].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
3612 descriptorPoolSizes[i].descriptorCount = DESCRIPTOR_POOL_SIZE;
3613 }
3614
3615 for (Uint32 i = descriptorSetLayout->writeStorageTextureCount; i < descriptorSetLayout->writeStorageTextureCount + descriptorSetLayout->writeStorageBufferCount; i += 1) {
3616 descriptorPoolSizes[i].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
3617 descriptorPoolSizes[i].descriptorCount = DESCRIPTOR_POOL_SIZE;
3618 }
3619
3620 // Category 3
3621 for (Uint32 i = 0; i < descriptorSetLayout->uniformBufferCount; i += 1) {
3622 descriptorPoolSizes[i].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
3623 descriptorPoolSizes[i].descriptorCount = DESCRIPTOR_POOL_SIZE;
3624 }
3625
3626 descriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
3627 descriptorPoolInfo.pNext = NULL;
3628 descriptorPoolInfo.flags = 0;
3629 descriptorPoolInfo.maxSets = DESCRIPTOR_POOL_SIZE;
3630 descriptorPoolInfo.poolSizeCount =
3631 descriptorSetLayout->samplerCount +
3632 descriptorSetLayout->storageTextureCount +
3633 descriptorSetLayout->storageBufferCount +
3634 descriptorSetLayout->writeStorageTextureCount +
3635 descriptorSetLayout->writeStorageBufferCount +
3636 descriptorSetLayout->uniformBufferCount;
3637 descriptorPoolInfo.pPoolSizes = descriptorPoolSizes;
3638
3639 vulkanResult = renderer->vkCreateDescriptorPool(
3640 renderer->logicalDevice,
3641 &descriptorPoolInfo,
3642 NULL,
3643 &pool);
3644
3645 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateDescriptorPool, false);
3646
3647 descriptorSetPool->poolCount += 1;
3648 descriptorSetPool->descriptorPools = SDL_realloc(
3649 descriptorSetPool->descriptorPools,
3650 sizeof(VkDescriptorPool) * descriptorSetPool->poolCount);
3651
3652 descriptorSetPool->descriptorPools[descriptorSetPool->poolCount - 1] = pool;
3653
3654 descriptorSetPool->descriptorSets = SDL_realloc(
3655 descriptorSetPool->descriptorSets,
3656 sizeof(VkDescriptorSet) * descriptorSetPool->poolCount * DESCRIPTOR_POOL_SIZE);
3657
3658 if (!VULKAN_INTERNAL_AllocateDescriptorSets(
3659 renderer,
3660 pool,
3661 descriptorSetLayout->descriptorSetLayout,
3662 DESCRIPTOR_POOL_SIZE,
3663 &descriptorSetPool->descriptorSets[descriptorSetPool->descriptorSetCount])) {
3664 return false;
3665 }
3666
3667 descriptorSetPool->descriptorSetCount += DESCRIPTOR_POOL_SIZE;
3668
3669 return true;
3670}
3671
3672// NOTE: these categories should be mutually exclusive
3673static DescriptorSetLayout *VULKAN_INTERNAL_FetchDescriptorSetLayout(
3674 VulkanRenderer *renderer,
3675 VkShaderStageFlagBits shaderStage,
3676 // Category 1: read resources
3677 Uint32 samplerCount,
3678 Uint32 storageTextureCount,
3679 Uint32 storageBufferCount,
3680 // Category 2: write resources
3681 Uint32 writeStorageTextureCount,
3682 Uint32 writeStorageBufferCount,
3683 // Category 3: uniform buffers
3684 Uint32 uniformBufferCount)
3685{
3686 DescriptorSetLayoutHashTableKey key;
3687 SDL_zero(key);
3688 DescriptorSetLayout *layout = NULL;
3689
3690 key.shaderStage = shaderStage;
3691 key.samplerCount = samplerCount;
3692 key.storageTextureCount = storageTextureCount;
3693 key.storageBufferCount = storageBufferCount;
3694 key.writeStorageTextureCount = writeStorageTextureCount;
3695 key.writeStorageBufferCount = writeStorageBufferCount;
3696 key.uniformBufferCount = uniformBufferCount;
3697
3698 if (SDL_FindInHashTable(
3699 renderer->descriptorSetLayoutHashTable,
3700 (const void *)&key,
3701 (const void **)&layout)) {
3702 return layout;
3703 }
3704
3705 VkDescriptorSetLayout descriptorSetLayout;
3706 VkDescriptorSetLayoutBinding descriptorSetLayoutBindings[
3707 MAX_TEXTURE_SAMPLERS_PER_STAGE +
3708 MAX_STORAGE_TEXTURES_PER_STAGE +
3709 MAX_STORAGE_BUFFERS_PER_STAGE +
3710 MAX_COMPUTE_WRITE_TEXTURES +
3711 MAX_COMPUTE_WRITE_BUFFERS];
3712
3713 VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo;
3714 descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
3715 descriptorSetLayoutCreateInfo.pNext = NULL;
3716 descriptorSetLayoutCreateInfo.flags = 0;
3717
3718 // Category 1
3719 for (Uint32 i = 0; i < samplerCount; i += 1) {
3720 descriptorSetLayoutBindings[i].binding = i;
3721 descriptorSetLayoutBindings[i].descriptorCount = 1;
3722 descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
3723 descriptorSetLayoutBindings[i].stageFlags = shaderStage;
3724 descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
3725 }
3726
3727 for (Uint32 i = samplerCount; i < samplerCount + storageTextureCount; i += 1) {
3728 descriptorSetLayoutBindings[i].binding = i;
3729 descriptorSetLayoutBindings[i].descriptorCount = 1;
3730 descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; // Yes, we are declaring the storage image as a sampled image, because shaders are stupid.
3731 descriptorSetLayoutBindings[i].stageFlags = shaderStage;
3732 descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
3733 }
3734
3735 for (Uint32 i = samplerCount + storageTextureCount; i < samplerCount + storageTextureCount + storageBufferCount; i += 1) {
3736 descriptorSetLayoutBindings[i].binding = i;
3737 descriptorSetLayoutBindings[i].descriptorCount = 1;
3738 descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
3739 descriptorSetLayoutBindings[i].stageFlags = shaderStage;
3740 descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
3741 }
3742
3743 // Category 2
3744 for (Uint32 i = 0; i < writeStorageTextureCount; i += 1) {
3745 descriptorSetLayoutBindings[i].binding = i;
3746 descriptorSetLayoutBindings[i].descriptorCount = 1;
3747 descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
3748 descriptorSetLayoutBindings[i].stageFlags = shaderStage;
3749 descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
3750 }
3751
3752 for (Uint32 i = writeStorageTextureCount; i < writeStorageTextureCount + writeStorageBufferCount; i += 1) {
3753 descriptorSetLayoutBindings[i].binding = i;
3754 descriptorSetLayoutBindings[i].descriptorCount = 1;
3755 descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
3756 descriptorSetLayoutBindings[i].stageFlags = shaderStage;
3757 descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
3758 }
3759
3760 // Category 3
3761 for (Uint32 i = 0; i < uniformBufferCount; i += 1) {
3762 descriptorSetLayoutBindings[i].binding = i;
3763 descriptorSetLayoutBindings[i].descriptorCount = 1;
3764 descriptorSetLayoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
3765 descriptorSetLayoutBindings[i].stageFlags = shaderStage;
3766 descriptorSetLayoutBindings[i].pImmutableSamplers = NULL;
3767 }
3768
3769 descriptorSetLayoutCreateInfo.pBindings = descriptorSetLayoutBindings;
3770 descriptorSetLayoutCreateInfo.bindingCount =
3771 samplerCount +
3772 storageTextureCount +
3773 storageBufferCount +
3774 writeStorageTextureCount +
3775 writeStorageBufferCount +
3776 uniformBufferCount;
3777
3778 VkResult vulkanResult = renderer->vkCreateDescriptorSetLayout(
3779 renderer->logicalDevice,
3780 &descriptorSetLayoutCreateInfo,
3781 NULL,
3782 &descriptorSetLayout);
3783
3784 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateDescriptorSetLayout, NULL);
3785
3786 layout = SDL_malloc(sizeof(DescriptorSetLayout));
3787 layout->descriptorSetLayout = descriptorSetLayout;
3788
3789 layout->samplerCount = samplerCount;
3790 layout->storageBufferCount = storageBufferCount;
3791 layout->storageTextureCount = storageTextureCount;
3792 layout->writeStorageBufferCount = writeStorageBufferCount;
3793 layout->writeStorageTextureCount = writeStorageTextureCount;
3794 layout->uniformBufferCount = uniformBufferCount;
3795
3796 layout->ID = SDL_AtomicIncRef(&renderer->layoutResourceID);
3797
3798 DescriptorSetLayoutHashTableKey *allocedKey = SDL_malloc(sizeof(DescriptorSetLayoutHashTableKey));
3799 SDL_memcpy(allocedKey, &key, sizeof(DescriptorSetLayoutHashTableKey));
3800
3801 SDL_InsertIntoHashTable(
3802 renderer->descriptorSetLayoutHashTable,
3803 (const void *)allocedKey,
3804 (const void *)layout, true);
3805
3806 return layout;
3807}
3808
3809static VulkanGraphicsPipelineResourceLayout *VULKAN_INTERNAL_FetchGraphicsPipelineResourceLayout(
3810 VulkanRenderer *renderer,
3811 VulkanShader *vertexShader,
3812 VulkanShader *fragmentShader)
3813{
3814 GraphicsPipelineResourceLayoutHashTableKey key;
3815 SDL_zero(key);
3816 VulkanGraphicsPipelineResourceLayout *pipelineResourceLayout = NULL;
3817
3818 key.vertexSamplerCount = vertexShader->numSamplers;
3819 key.vertexStorageTextureCount = vertexShader->numStorageTextures;
3820 key.vertexStorageBufferCount = vertexShader->numStorageBuffers;
3821 key.vertexUniformBufferCount = vertexShader->numUniformBuffers;
3822 key.fragmentSamplerCount = fragmentShader->numSamplers;
3823 key.fragmentStorageTextureCount = fragmentShader->numStorageTextures;
3824 key.fragmentStorageBufferCount = fragmentShader->numStorageBuffers;
3825 key.fragmentUniformBufferCount = fragmentShader->numUniformBuffers;
3826 if (SDL_FindInHashTable(
3827 renderer->graphicsPipelineResourceLayoutHashTable,
3828 (const void *)&key,
3829 (const void **)&pipelineResourceLayout)) {
3830 return pipelineResourceLayout;
3831 }
3832
3833 VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo;
3834 VkDescriptorSetLayout descriptorSetLayouts[4];
3835 VkResult vulkanResult;
3836
3837 pipelineResourceLayout = SDL_calloc(1, sizeof(VulkanGraphicsPipelineResourceLayout));
3838
3839 pipelineResourceLayout->descriptorSetLayouts[0] = VULKAN_INTERNAL_FetchDescriptorSetLayout(
3840 renderer,
3841 VK_SHADER_STAGE_VERTEX_BIT,
3842 vertexShader->numSamplers,
3843 vertexShader->numStorageTextures,
3844 vertexShader->numStorageBuffers,
3845 0,
3846 0,
3847 0);
3848
3849 pipelineResourceLayout->descriptorSetLayouts[1] = VULKAN_INTERNAL_FetchDescriptorSetLayout(
3850 renderer,
3851 VK_SHADER_STAGE_VERTEX_BIT,
3852 0,
3853 0,
3854 0,
3855 0,
3856 0,
3857 vertexShader->numUniformBuffers);
3858
3859 pipelineResourceLayout->descriptorSetLayouts[2] = VULKAN_INTERNAL_FetchDescriptorSetLayout(
3860 renderer,
3861 VK_SHADER_STAGE_FRAGMENT_BIT,
3862 fragmentShader->numSamplers,
3863 fragmentShader->numStorageTextures,
3864 fragmentShader->numStorageBuffers,
3865 0,
3866 0,
3867 0);
3868
3869 pipelineResourceLayout->descriptorSetLayouts[3] = VULKAN_INTERNAL_FetchDescriptorSetLayout(
3870 renderer,
3871 VK_SHADER_STAGE_FRAGMENT_BIT,
3872 0,
3873 0,
3874 0,
3875 0,
3876 0,
3877 fragmentShader->numUniformBuffers);
3878
3879 descriptorSetLayouts[0] = pipelineResourceLayout->descriptorSetLayouts[0]->descriptorSetLayout;
3880 descriptorSetLayouts[1] = pipelineResourceLayout->descriptorSetLayouts[1]->descriptorSetLayout;
3881 descriptorSetLayouts[2] = pipelineResourceLayout->descriptorSetLayouts[2]->descriptorSetLayout;
3882 descriptorSetLayouts[3] = pipelineResourceLayout->descriptorSetLayouts[3]->descriptorSetLayout;
3883
3884 pipelineResourceLayout->vertexSamplerCount = vertexShader->numSamplers;
3885 pipelineResourceLayout->vertexStorageTextureCount = vertexShader->numStorageTextures;
3886 pipelineResourceLayout->vertexStorageBufferCount = vertexShader->numStorageBuffers;
3887 pipelineResourceLayout->vertexUniformBufferCount = vertexShader->numUniformBuffers;
3888
3889 pipelineResourceLayout->fragmentSamplerCount = fragmentShader->numSamplers;
3890 pipelineResourceLayout->fragmentStorageTextureCount = fragmentShader->numStorageTextures;
3891 pipelineResourceLayout->fragmentStorageBufferCount = fragmentShader->numStorageBuffers;
3892 pipelineResourceLayout->fragmentUniformBufferCount = fragmentShader->numUniformBuffers;
3893
3894 // Create the pipeline layout
3895
3896 pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
3897 pipelineLayoutCreateInfo.pNext = NULL;
3898 pipelineLayoutCreateInfo.flags = 0;
3899 pipelineLayoutCreateInfo.setLayoutCount = 4;
3900 pipelineLayoutCreateInfo.pSetLayouts = descriptorSetLayouts;
3901 pipelineLayoutCreateInfo.pushConstantRangeCount = 0;
3902 pipelineLayoutCreateInfo.pPushConstantRanges = NULL;
3903
3904 vulkanResult = renderer->vkCreatePipelineLayout(
3905 renderer->logicalDevice,
3906 &pipelineLayoutCreateInfo,
3907 NULL,
3908 &pipelineResourceLayout->pipelineLayout);
3909
3910 if (vulkanResult != VK_SUCCESS) {
3911 VULKAN_INTERNAL_DestroyGraphicsPipelineResourceLayout(renderer, pipelineResourceLayout);
3912 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreatePipelineLayout, NULL);
3913 }
3914
3915 GraphicsPipelineResourceLayoutHashTableKey *allocedKey = SDL_malloc(sizeof(GraphicsPipelineResourceLayoutHashTableKey));
3916 SDL_memcpy(allocedKey, &key, sizeof(GraphicsPipelineResourceLayoutHashTableKey));
3917
3918 SDL_InsertIntoHashTable(
3919 renderer->graphicsPipelineResourceLayoutHashTable,
3920 (const void *)allocedKey,
3921 (const void *)pipelineResourceLayout, true);
3922
3923 return pipelineResourceLayout;
3924}
3925
3926static VulkanComputePipelineResourceLayout *VULKAN_INTERNAL_FetchComputePipelineResourceLayout(
3927 VulkanRenderer *renderer,
3928 const SDL_GPUComputePipelineCreateInfo *createinfo)
3929{
3930 ComputePipelineResourceLayoutHashTableKey key;
3931 SDL_zero(key);
3932 VulkanComputePipelineResourceLayout *pipelineResourceLayout = NULL;
3933
3934 key.samplerCount = createinfo->num_samplers;
3935 key.readonlyStorageTextureCount = createinfo->num_readonly_storage_textures;
3936 key.readonlyStorageBufferCount = createinfo->num_readonly_storage_buffers;
3937 key.readWriteStorageTextureCount = createinfo->num_readwrite_storage_textures;
3938 key.readWriteStorageBufferCount = createinfo->num_readwrite_storage_buffers;
3939 key.uniformBufferCount = createinfo->num_uniform_buffers;
3940
3941 if (SDL_FindInHashTable(
3942 renderer->computePipelineResourceLayoutHashTable,
3943 (const void *)&key,
3944 (const void **)&pipelineResourceLayout)) {
3945 return pipelineResourceLayout;
3946 }
3947
3948 VkDescriptorSetLayout descriptorSetLayouts[3];
3949 VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo;
3950 VkResult vulkanResult;
3951
3952 pipelineResourceLayout = SDL_calloc(1, sizeof(VulkanComputePipelineResourceLayout));
3953
3954 pipelineResourceLayout->descriptorSetLayouts[0] = VULKAN_INTERNAL_FetchDescriptorSetLayout(
3955 renderer,
3956 VK_SHADER_STAGE_COMPUTE_BIT,
3957 createinfo->num_samplers,
3958 createinfo->num_readonly_storage_textures,
3959 createinfo->num_readonly_storage_buffers,
3960 0,
3961 0,
3962 0);
3963
3964 pipelineResourceLayout->descriptorSetLayouts[1] = VULKAN_INTERNAL_FetchDescriptorSetLayout(
3965 renderer,
3966 VK_SHADER_STAGE_COMPUTE_BIT,
3967 0,
3968 0,
3969 0,
3970 createinfo->num_readwrite_storage_textures,
3971 createinfo->num_readwrite_storage_buffers,
3972 0);
3973
3974 pipelineResourceLayout->descriptorSetLayouts[2] = VULKAN_INTERNAL_FetchDescriptorSetLayout(
3975 renderer,
3976 VK_SHADER_STAGE_COMPUTE_BIT,
3977 0,
3978 0,
3979 0,
3980 0,
3981 0,
3982 createinfo->num_uniform_buffers);
3983
3984 descriptorSetLayouts[0] = pipelineResourceLayout->descriptorSetLayouts[0]->descriptorSetLayout;
3985 descriptorSetLayouts[1] = pipelineResourceLayout->descriptorSetLayouts[1]->descriptorSetLayout;
3986 descriptorSetLayouts[2] = pipelineResourceLayout->descriptorSetLayouts[2]->descriptorSetLayout;
3987
3988 pipelineResourceLayout->numSamplers = createinfo->num_samplers;
3989 pipelineResourceLayout->numReadonlyStorageTextures = createinfo->num_readonly_storage_textures;
3990 pipelineResourceLayout->numReadonlyStorageBuffers = createinfo->num_readonly_storage_buffers;
3991 pipelineResourceLayout->numReadWriteStorageTextures = createinfo->num_readwrite_storage_textures;
3992 pipelineResourceLayout->numReadWriteStorageBuffers = createinfo->num_readwrite_storage_buffers;
3993 pipelineResourceLayout->numUniformBuffers = createinfo->num_uniform_buffers;
3994
3995 // Create the pipeline layout
3996
3997 pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
3998 pipelineLayoutCreateInfo.pNext = NULL;
3999 pipelineLayoutCreateInfo.flags = 0;
4000 pipelineLayoutCreateInfo.setLayoutCount = 3;
4001 pipelineLayoutCreateInfo.pSetLayouts = descriptorSetLayouts;
4002 pipelineLayoutCreateInfo.pushConstantRangeCount = 0;
4003 pipelineLayoutCreateInfo.pPushConstantRanges = NULL;
4004
4005 vulkanResult = renderer->vkCreatePipelineLayout(
4006 renderer->logicalDevice,
4007 &pipelineLayoutCreateInfo,
4008 NULL,
4009 &pipelineResourceLayout->pipelineLayout);
4010
4011 if (vulkanResult != VK_SUCCESS) {
4012 VULKAN_INTERNAL_DestroyComputePipelineResourceLayout(renderer, pipelineResourceLayout);
4013 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreatePipelineLayout, NULL);
4014 }
4015
4016 ComputePipelineResourceLayoutHashTableKey *allocedKey = SDL_malloc(sizeof(ComputePipelineResourceLayoutHashTableKey));
4017 SDL_memcpy(allocedKey, &key, sizeof(ComputePipelineResourceLayoutHashTableKey));
4018
4019 SDL_InsertIntoHashTable(
4020 renderer->computePipelineResourceLayoutHashTable,
4021 (const void *)allocedKey,
4022 (const void *)pipelineResourceLayout, true);
4023
4024 return pipelineResourceLayout;
4025}
4026
4027// Data Buffer
4028
4029static VulkanBuffer *VULKAN_INTERNAL_CreateBuffer(
4030 VulkanRenderer *renderer,
4031 VkDeviceSize size,
4032 SDL_GPUBufferUsageFlags usageFlags,
4033 VulkanBufferType type,
4034 bool dedicated,
4035 const char *debugName)
4036{
4037 VulkanBuffer *buffer;
4038 VkResult vulkanResult;
4039 VkBufferCreateInfo createinfo;
4040 VkBufferUsageFlags vulkanUsageFlags = 0;
4041 Uint8 bindResult;
4042
4043 if (usageFlags & SDL_GPU_BUFFERUSAGE_VERTEX) {
4044 vulkanUsageFlags |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
4045 }
4046
4047 if (usageFlags & SDL_GPU_BUFFERUSAGE_INDEX) {
4048 vulkanUsageFlags |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
4049 }
4050
4051 if (usageFlags & (SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ |
4052 SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ |
4053 SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE)) {
4054 vulkanUsageFlags |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
4055 }
4056
4057 if (usageFlags & SDL_GPU_BUFFERUSAGE_INDIRECT) {
4058 vulkanUsageFlags |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
4059 }
4060
4061 if (type == VULKAN_BUFFER_TYPE_UNIFORM) {
4062 vulkanUsageFlags |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
4063 } else {
4064 // GPU buffers need transfer bits for defrag, transfer buffers need them for transfers
4065 vulkanUsageFlags |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4066 }
4067
4068 buffer = SDL_calloc(1, sizeof(VulkanBuffer));
4069
4070 buffer->size = size;
4071 buffer->usage = usageFlags;
4072 buffer->type = type;
4073 buffer->markedForDestroy = false;
4074 buffer->transitioned = false;
4075
4076 createinfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4077 createinfo.pNext = NULL;
4078 createinfo.flags = 0;
4079 createinfo.size = size;
4080 createinfo.usage = vulkanUsageFlags;
4081 createinfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
4082 createinfo.queueFamilyIndexCount = 1;
4083 createinfo.pQueueFamilyIndices = &renderer->queueFamilyIndex;
4084
4085 // Set transfer bits so we can defrag
4086 createinfo.usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4087
4088 vulkanResult = renderer->vkCreateBuffer(
4089 renderer->logicalDevice,
4090 &createinfo,
4091 NULL,
4092 &buffer->buffer);
4093
4094 if (vulkanResult != VK_SUCCESS) {
4095 SDL_free(buffer);
4096 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateBuffer, NULL);
4097 }
4098
4099 bindResult = VULKAN_INTERNAL_BindMemoryForBuffer(
4100 renderer,
4101 buffer->buffer,
4102 buffer->size,
4103 buffer->type,
4104 dedicated,
4105 &buffer->usedRegion);
4106
4107 if (bindResult != 1) {
4108 renderer->vkDestroyBuffer(
4109 renderer->logicalDevice,
4110 buffer->buffer,
4111 NULL);
4112
4113 SDL_free(buffer);
4114 return NULL;
4115 }
4116
4117 buffer->usedRegion->vulkanBuffer = buffer; // lol
4118
4119 SDL_SetAtomicInt(&buffer->referenceCount, 0);
4120
4121 if (renderer->debugMode && renderer->supportsDebugUtils && debugName != NULL) {
4122 VkDebugUtilsObjectNameInfoEXT nameInfo;
4123 nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
4124 nameInfo.pNext = NULL;
4125 nameInfo.pObjectName = debugName;
4126 nameInfo.objectType = VK_OBJECT_TYPE_BUFFER;
4127 nameInfo.objectHandle = (uint64_t)buffer->buffer;
4128
4129 renderer->vkSetDebugUtilsObjectNameEXT(
4130 renderer->logicalDevice,
4131 &nameInfo);
4132 }
4133
4134 return buffer;
4135}
4136
4137static VulkanBufferContainer *VULKAN_INTERNAL_CreateBufferContainer(
4138 VulkanRenderer *renderer,
4139 VkDeviceSize size,
4140 SDL_GPUBufferUsageFlags usageFlags,
4141 VulkanBufferType type,
4142 bool dedicated,
4143 const char *debugName)
4144{
4145 VulkanBufferContainer *bufferContainer;
4146 VulkanBuffer *buffer;
4147
4148 buffer = VULKAN_INTERNAL_CreateBuffer(
4149 renderer,
4150 size,
4151 usageFlags,
4152 type,
4153 dedicated,
4154 debugName);
4155
4156 if (buffer == NULL) {
4157 return NULL;
4158 }
4159
4160 bufferContainer = SDL_calloc(1, sizeof(VulkanBufferContainer));
4161
4162 bufferContainer->activeBuffer = buffer;
4163 buffer->container = bufferContainer;
4164 buffer->containerIndex = 0;
4165
4166 bufferContainer->bufferCapacity = 1;
4167 bufferContainer->bufferCount = 1;
4168 bufferContainer->buffers = SDL_calloc(bufferContainer->bufferCapacity, sizeof(VulkanBuffer *));
4169 bufferContainer->buffers[0] = bufferContainer->activeBuffer;
4170 bufferContainer->dedicated = dedicated;
4171 bufferContainer->debugName = NULL;
4172
4173 if (debugName != NULL) {
4174 bufferContainer->debugName = SDL_strdup(debugName);
4175 }
4176
4177 return bufferContainer;
4178}
4179
4180// Texture Subresource Utilities
4181
4182static Uint32 VULKAN_INTERNAL_GetTextureSubresourceIndex(
4183 Uint32 mipLevel,
4184 Uint32 layer,
4185 Uint32 numLevels)
4186{
4187 return mipLevel + (layer * numLevels);
4188}
4189
4190static VulkanTextureSubresource *VULKAN_INTERNAL_FetchTextureSubresource(
4191 VulkanTextureContainer *textureContainer,
4192 Uint32 layer,
4193 Uint32 level)
4194{
4195 Uint32 index = VULKAN_INTERNAL_GetTextureSubresourceIndex(
4196 level,
4197 layer,
4198 textureContainer->header.info.num_levels);
4199
4200 return &textureContainer->activeTexture->subresources[index];
4201}
4202
4203static bool VULKAN_INTERNAL_CreateRenderTargetView(
4204 VulkanRenderer *renderer,
4205 VulkanTexture *texture,
4206 Uint32 layerOrDepth,
4207 Uint32 level,
4208 VkFormat format,
4209 VkComponentMapping swizzle,
4210 VkImageView *pView)
4211{
4212 VkResult vulkanResult;
4213 VkImageViewCreateInfo imageViewCreateInfo;
4214
4215 // create framebuffer compatible views for RenderTarget
4216 imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
4217 imageViewCreateInfo.pNext = NULL;
4218 imageViewCreateInfo.flags = 0;
4219 imageViewCreateInfo.image = texture->image;
4220 imageViewCreateInfo.format = format;
4221 imageViewCreateInfo.components = swizzle;
4222 imageViewCreateInfo.subresourceRange.aspectMask = texture->aspectFlags;
4223 imageViewCreateInfo.subresourceRange.baseMipLevel = level;
4224 imageViewCreateInfo.subresourceRange.levelCount = 1;
4225 imageViewCreateInfo.subresourceRange.baseArrayLayer = layerOrDepth;
4226 imageViewCreateInfo.subresourceRange.layerCount = 1;
4227 imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
4228
4229 vulkanResult = renderer->vkCreateImageView(
4230 renderer->logicalDevice,
4231 &imageViewCreateInfo,
4232 NULL,
4233 pView);
4234
4235 if (vulkanResult != VK_SUCCESS) {
4236 *pView = (VkImageView)VK_NULL_HANDLE;
4237 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateImageView, false);
4238 }
4239
4240 return true;
4241}
4242
4243static bool VULKAN_INTERNAL_CreateSubresourceView(
4244 VulkanRenderer *renderer,
4245 const SDL_GPUTextureCreateInfo *createinfo,
4246 VulkanTexture *texture,
4247 Uint32 layer,
4248 Uint32 level,
4249 VkComponentMapping swizzle,
4250 VkImageView *pView)
4251{
4252 VkResult vulkanResult;
4253 VkImageViewCreateInfo imageViewCreateInfo;
4254
4255 // create framebuffer compatible views for RenderTarget
4256 imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
4257 imageViewCreateInfo.pNext = NULL;
4258 imageViewCreateInfo.flags = 0;
4259 imageViewCreateInfo.image = texture->image;
4260 imageViewCreateInfo.format = SDLToVK_TextureFormat[createinfo->format];
4261 imageViewCreateInfo.components = swizzle;
4262 imageViewCreateInfo.subresourceRange.aspectMask = texture->aspectFlags;
4263 imageViewCreateInfo.subresourceRange.baseMipLevel = level;
4264 imageViewCreateInfo.subresourceRange.levelCount = 1;
4265 imageViewCreateInfo.subresourceRange.baseArrayLayer = layer;
4266 imageViewCreateInfo.subresourceRange.layerCount = 1;
4267 imageViewCreateInfo.viewType = (createinfo->type == SDL_GPU_TEXTURETYPE_3D) ? VK_IMAGE_VIEW_TYPE_3D : VK_IMAGE_VIEW_TYPE_2D;
4268
4269 vulkanResult = renderer->vkCreateImageView(
4270 renderer->logicalDevice,
4271 &imageViewCreateInfo,
4272 NULL,
4273 pView);
4274
4275 if (vulkanResult != VK_SUCCESS) {
4276 *pView = (VkImageView)VK_NULL_HANDLE;
4277 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateImageView, false);
4278 }
4279
4280 return true;
4281}
4282
4283// Swapchain
4284
4285static bool VULKAN_INTERNAL_QuerySwapchainSupport(
4286 VulkanRenderer *renderer,
4287 VkPhysicalDevice physicalDevice,
4288 VkSurfaceKHR surface,
4289 SwapchainSupportDetails *outputDetails)
4290{
4291 VkResult result;
4292 VkBool32 supportsPresent;
4293
4294 renderer->vkGetPhysicalDeviceSurfaceSupportKHR(
4295 physicalDevice,
4296 renderer->queueFamilyIndex,
4297 surface,
4298 &supportsPresent);
4299
4300 // Initialize these in case anything fails
4301 outputDetails->formatsLength = 0;
4302 outputDetails->presentModesLength = 0;
4303
4304 if (!supportsPresent) {
4305 SET_STRING_ERROR_AND_RETURN("This surface does not support presenting!", false);
4306 }
4307
4308 // Run the device surface queries
4309 result = renderer->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
4310 physicalDevice,
4311 surface,
4312 &outputDetails->capabilities);
4313 CHECK_VULKAN_ERROR_AND_RETURN(result, vkGetPhysicalDeviceSurfaceCapabilitiesKHR, false);
4314
4315 if (!(outputDetails->capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)) {
4316 SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Opaque presentation unsupported! Expect weird transparency bugs!");
4317 }
4318
4319 result = renderer->vkGetPhysicalDeviceSurfaceFormatsKHR(
4320 physicalDevice,
4321 surface,
4322 &outputDetails->formatsLength,
4323 NULL);
4324 CHECK_VULKAN_ERROR_AND_RETURN(result, vkGetPhysicalDeviceSurfaceFormatsKHR, false);
4325 result = renderer->vkGetPhysicalDeviceSurfacePresentModesKHR(
4326 physicalDevice,
4327 surface,
4328 &outputDetails->presentModesLength,
4329 NULL);
4330 CHECK_VULKAN_ERROR_AND_RETURN(result, vkGetPhysicalDeviceSurfacePresentModesKHR, false);
4331
4332 // Generate the arrays, if applicable
4333
4334 outputDetails->formats = NULL;
4335 if (outputDetails->formatsLength != 0) {
4336 outputDetails->formats = (VkSurfaceFormatKHR *)SDL_malloc(
4337 sizeof(VkSurfaceFormatKHR) * outputDetails->formatsLength);
4338
4339 if (!outputDetails->formats) { // OOM
4340 return false;
4341 }
4342
4343 result = renderer->vkGetPhysicalDeviceSurfaceFormatsKHR(
4344 physicalDevice,
4345 surface,
4346 &outputDetails->formatsLength,
4347 outputDetails->formats);
4348 if (result != VK_SUCCESS) {
4349 SDL_free(outputDetails->formats);
4350 CHECK_VULKAN_ERROR_AND_RETURN(result, vkGetPhysicalDeviceSurfaceFormatsKHR, false);
4351 }
4352 }
4353
4354 outputDetails->presentModes = NULL;
4355 if (outputDetails->presentModesLength != 0) {
4356 outputDetails->presentModes = (VkPresentModeKHR *)SDL_malloc(
4357 sizeof(VkPresentModeKHR) * outputDetails->presentModesLength);
4358
4359 if (!outputDetails->presentModes) { // OOM
4360 SDL_free(outputDetails->formats);
4361 return false;
4362 }
4363
4364 result = renderer->vkGetPhysicalDeviceSurfacePresentModesKHR(
4365 physicalDevice,
4366 surface,
4367 &outputDetails->presentModesLength,
4368 outputDetails->presentModes);
4369 if (result != VK_SUCCESS) {
4370 SDL_free(outputDetails->formats);
4371 SDL_free(outputDetails->presentModes);
4372 CHECK_VULKAN_ERROR_AND_RETURN(result, vkGetPhysicalDeviceSurfacePresentModesKHR, false);
4373 }
4374 }
4375
4376 /* If we made it here, all the queries were successful. This does NOT
4377 * necessarily mean there are any supported formats or present modes!
4378 */
4379 return true;
4380}
4381
4382static bool VULKAN_INTERNAL_VerifySwapSurfaceFormat(
4383 VkFormat desiredFormat,
4384 VkColorSpaceKHR desiredColorSpace,
4385 VkSurfaceFormatKHR *availableFormats,
4386 Uint32 availableFormatsLength)
4387{
4388 Uint32 i;
4389 for (i = 0; i < availableFormatsLength; i += 1) {
4390 if (availableFormats[i].format == desiredFormat &&
4391 availableFormats[i].colorSpace == desiredColorSpace) {
4392 return true;
4393 }
4394 }
4395 return false;
4396}
4397
4398static bool VULKAN_INTERNAL_VerifySwapPresentMode(
4399 VkPresentModeKHR presentMode,
4400 const VkPresentModeKHR *availablePresentModes,
4401 Uint32 availablePresentModesLength)
4402{
4403 Uint32 i;
4404 for (i = 0; i < availablePresentModesLength; i += 1) {
4405 if (availablePresentModes[i] == presentMode) {
4406 return true;
4407 }
4408 }
4409 return false;
4410}
4411
4412/* It would be nice if VULKAN_INTERNAL_CreateSwapchain could return a bool.
4413 * Unfortunately, some Win32 NVIDIA drivers are stupid
4414 * and will return surface extents of (0, 0)
4415 * in certain edge cases, and the swapchain extents are not allowed to be 0.
4416 * In this case, the client probably still wants to claim the window
4417 * or recreate the swapchain, so we should return 2 to indicate retry.
4418 * -cosmonaut
4419 */
4420#define VULKAN_INTERNAL_TRY_AGAIN 2
4421
4422static Uint32 VULKAN_INTERNAL_CreateSwapchain(
4423 VulkanRenderer *renderer,
4424 WindowData *windowData)
4425{
4426 VkResult vulkanResult;
4427 VkSwapchainCreateInfoKHR swapchainCreateInfo;
4428 VkImage *swapchainImages;
4429 VkSemaphoreCreateInfo semaphoreCreateInfo;
4430 SwapchainSupportDetails swapchainSupportDetails;
4431 bool hasValidSwapchainComposition, hasValidPresentMode;
4432 Uint32 i;
4433
4434 windowData->frameCounter = 0;
4435
4436 SDL_VideoDevice *_this = SDL_GetVideoDevice();
4437 SDL_assert(_this && _this->Vulkan_CreateSurface);
4438
4439 // Each swapchain must have its own surface.
4440 if (!_this->Vulkan_CreateSurface(
4441 _this,
4442 windowData->window,
4443 renderer->instance,
4444 NULL, // FIXME: VAllocationCallbacks
4445 &windowData->surface)) {
4446 return false;
4447 }
4448 SDL_assert(windowData->surface);
4449
4450 if (!VULKAN_INTERNAL_QuerySwapchainSupport(
4451 renderer,
4452 renderer->physicalDevice,
4453 windowData->surface,
4454 &swapchainSupportDetails)) {
4455 renderer->vkDestroySurfaceKHR(
4456 renderer->instance,
4457 windowData->surface,
4458 NULL);
4459 windowData->surface = VK_NULL_HANDLE;
4460 if (swapchainSupportDetails.formatsLength > 0) {
4461 SDL_free(swapchainSupportDetails.formats);
4462 }
4463 if (swapchainSupportDetails.presentModesLength > 0) {
4464 SDL_free(swapchainSupportDetails.presentModes);
4465 }
4466 return false;
4467 }
4468
4469 // Verify that we can use the requested composition and present mode
4470 windowData->format = SwapchainCompositionToFormat[windowData->swapchainComposition];
4471 windowData->colorSpace = SwapchainCompositionToColorSpace[windowData->swapchainComposition];
4472 windowData->swapchainSwizzle = SwapchainCompositionSwizzle[windowData->swapchainComposition];
4473 windowData->usingFallbackFormat = false;
4474
4475 hasValidSwapchainComposition = VULKAN_INTERNAL_VerifySwapSurfaceFormat(
4476 windowData->format,
4477 windowData->colorSpace,
4478 swapchainSupportDetails.formats,
4479 swapchainSupportDetails.formatsLength);
4480
4481 if (!hasValidSwapchainComposition) {
4482 // Let's try again with the fallback format...
4483 windowData->format = SwapchainCompositionToFallbackFormat[windowData->swapchainComposition];
4484 windowData->usingFallbackFormat = true;
4485 hasValidSwapchainComposition = VULKAN_INTERNAL_VerifySwapSurfaceFormat(
4486 windowData->format,
4487 windowData->colorSpace,
4488 swapchainSupportDetails.formats,
4489 swapchainSupportDetails.formatsLength);
4490 }
4491
4492 hasValidPresentMode = VULKAN_INTERNAL_VerifySwapPresentMode(
4493 SDLToVK_PresentMode[windowData->presentMode],
4494 swapchainSupportDetails.presentModes,
4495 swapchainSupportDetails.presentModesLength);
4496
4497 if (!hasValidSwapchainComposition || !hasValidPresentMode) {
4498 renderer->vkDestroySurfaceKHR(
4499 renderer->instance,
4500 windowData->surface,
4501 NULL);
4502 windowData->surface = VK_NULL_HANDLE;
4503
4504 if (swapchainSupportDetails.formatsLength > 0) {
4505 SDL_free(swapchainSupportDetails.formats);
4506 }
4507
4508 if (swapchainSupportDetails.presentModesLength > 0) {
4509 SDL_free(swapchainSupportDetails.presentModes);
4510 }
4511
4512 if (!hasValidSwapchainComposition) {
4513 SET_STRING_ERROR_AND_RETURN("Device does not support requested swapchain composition!", false);
4514 }
4515 if (!hasValidPresentMode) {
4516 SET_STRING_ERROR_AND_RETURN("Device does not support requested present_mode!", false);
4517 }
4518 return false;
4519 }
4520
4521 // NVIDIA + Win32 can return 0 extent when the window is minimized. Try again!
4522 if (swapchainSupportDetails.capabilities.currentExtent.width == 0 ||
4523 swapchainSupportDetails.capabilities.currentExtent.height == 0) {
4524 renderer->vkDestroySurfaceKHR(
4525 renderer->instance,
4526 windowData->surface,
4527 NULL);
4528 windowData->surface = VK_NULL_HANDLE;
4529 if (swapchainSupportDetails.formatsLength > 0) {
4530 SDL_free(swapchainSupportDetails.formats);
4531 }
4532 if (swapchainSupportDetails.presentModesLength > 0) {
4533 SDL_free(swapchainSupportDetails.presentModes);
4534 }
4535 return VULKAN_INTERNAL_TRY_AGAIN;
4536 }
4537
4538 Uint32 requestedImageCount = renderer->allowedFramesInFlight;
4539
4540#ifdef SDL_PLATFORM_APPLE
4541 windowData->width = swapchainSupportDetails.capabilities.currentExtent.width;
4542 windowData->height = swapchainSupportDetails.capabilities.currentExtent.height;
4543#else
4544 windowData->width = SDL_clamp(
4545 windowData->swapchainCreateWidth,
4546 swapchainSupportDetails.capabilities.minImageExtent.width,
4547 swapchainSupportDetails.capabilities.maxImageExtent.width);
4548 windowData->height = SDL_clamp(windowData->swapchainCreateHeight,
4549 swapchainSupportDetails.capabilities.minImageExtent.height,
4550 swapchainSupportDetails.capabilities.maxImageExtent.height);
4551#endif
4552
4553 if (swapchainSupportDetails.capabilities.maxImageCount > 0 &&
4554 requestedImageCount > swapchainSupportDetails.capabilities.maxImageCount) {
4555 requestedImageCount = swapchainSupportDetails.capabilities.maxImageCount;
4556 }
4557
4558 if (requestedImageCount < swapchainSupportDetails.capabilities.minImageCount) {
4559 requestedImageCount = swapchainSupportDetails.capabilities.minImageCount;
4560 }
4561
4562 if (windowData->presentMode == SDL_GPU_PRESENTMODE_MAILBOX) {
4563 /* Required for proper triple-buffering.
4564 * Note that this is below the above maxImageCount check!
4565 * If the driver advertises MAILBOX but does not support 3 swap
4566 * images, it's not real mailbox support, so let it fail hard.
4567 * -flibit
4568 */
4569 requestedImageCount = SDL_max(requestedImageCount, 3);
4570 }
4571
4572 swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
4573 swapchainCreateInfo.pNext = NULL;
4574 swapchainCreateInfo.flags = 0;
4575 swapchainCreateInfo.surface = windowData->surface;
4576 swapchainCreateInfo.minImageCount = requestedImageCount;
4577 swapchainCreateInfo.imageFormat = windowData->format;
4578 swapchainCreateInfo.imageColorSpace = windowData->colorSpace;
4579 swapchainCreateInfo.imageExtent.width = windowData->width;
4580 swapchainCreateInfo.imageExtent.height = windowData->height;
4581 swapchainCreateInfo.imageArrayLayers = 1;
4582 swapchainCreateInfo.imageUsage =
4583 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
4584 VK_IMAGE_USAGE_TRANSFER_DST_BIT;
4585 swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
4586 swapchainCreateInfo.queueFamilyIndexCount = 0;
4587 swapchainCreateInfo.pQueueFamilyIndices = NULL;
4588#ifdef SDL_PLATFORM_ANDROID
4589 swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
4590#else
4591 swapchainCreateInfo.preTransform = swapchainSupportDetails.capabilities.currentTransform;
4592#endif
4593 swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
4594 swapchainCreateInfo.presentMode = SDLToVK_PresentMode[windowData->presentMode];
4595 swapchainCreateInfo.clipped = VK_TRUE;
4596 swapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE;
4597
4598 vulkanResult = renderer->vkCreateSwapchainKHR(
4599 renderer->logicalDevice,
4600 &swapchainCreateInfo,
4601 NULL,
4602 &windowData->swapchain);
4603
4604 if (swapchainSupportDetails.formatsLength > 0) {
4605 SDL_free(swapchainSupportDetails.formats);
4606 }
4607 if (swapchainSupportDetails.presentModesLength > 0) {
4608 SDL_free(swapchainSupportDetails.presentModes);
4609 }
4610
4611 if (vulkanResult != VK_SUCCESS) {
4612 renderer->vkDestroySurfaceKHR(
4613 renderer->instance,
4614 windowData->surface,
4615 NULL);
4616 windowData->surface = VK_NULL_HANDLE;
4617 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSwapchainKHR, false);
4618 }
4619
4620 vulkanResult = renderer->vkGetSwapchainImagesKHR(
4621 renderer->logicalDevice,
4622 windowData->swapchain,
4623 &windowData->imageCount,
4624 NULL);
4625 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkGetSwapchainImagesKHR, false);
4626
4627 windowData->textureContainers = SDL_malloc(
4628 sizeof(VulkanTextureContainer) * windowData->imageCount);
4629
4630 if (!windowData->textureContainers) { // OOM
4631 renderer->vkDestroySurfaceKHR(
4632 renderer->instance,
4633 windowData->surface,
4634 NULL);
4635 renderer->vkDestroySwapchainKHR(
4636 renderer->logicalDevice,
4637 windowData->swapchain,
4638 NULL);
4639 windowData->surface = VK_NULL_HANDLE;
4640 windowData->swapchain = VK_NULL_HANDLE;
4641 return false;
4642 }
4643
4644 swapchainImages = SDL_stack_alloc(VkImage, windowData->imageCount);
4645
4646 vulkanResult = renderer->vkGetSwapchainImagesKHR(
4647 renderer->logicalDevice,
4648 windowData->swapchain,
4649 &windowData->imageCount,
4650 swapchainImages);
4651 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkGetSwapchainImagesKHR, false);
4652
4653 for (i = 0; i < windowData->imageCount; i += 1) {
4654
4655 // Initialize dummy container
4656 SDL_zero(windowData->textureContainers[i]);
4657 windowData->textureContainers[i].canBeCycled = false;
4658 windowData->textureContainers[i].header.info.width = windowData->width;
4659 windowData->textureContainers[i].header.info.height = windowData->height;
4660 windowData->textureContainers[i].header.info.layer_count_or_depth = 1;
4661 windowData->textureContainers[i].header.info.format = SwapchainCompositionToSDLFormat(
4662 windowData->swapchainComposition,
4663 windowData->usingFallbackFormat);
4664 windowData->textureContainers[i].header.info.type = SDL_GPU_TEXTURETYPE_2D;
4665 windowData->textureContainers[i].header.info.num_levels = 1;
4666 windowData->textureContainers[i].header.info.sample_count = SDL_GPU_SAMPLECOUNT_1;
4667 windowData->textureContainers[i].header.info.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
4668
4669 windowData->textureContainers[i].activeTexture = SDL_malloc(sizeof(VulkanTexture));
4670 windowData->textureContainers[i].activeTexture->image = swapchainImages[i];
4671
4672 // Swapchain memory is managed by the driver
4673 windowData->textureContainers[i].activeTexture->usedRegion = NULL;
4674
4675 windowData->textureContainers[i].activeTexture->swizzle = windowData->swapchainSwizzle;
4676 windowData->textureContainers[i].activeTexture->aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
4677 windowData->textureContainers[i].activeTexture->depth = 1;
4678 windowData->textureContainers[i].activeTexture->usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
4679 windowData->textureContainers[i].activeTexture->container = &windowData->textureContainers[i];
4680 SDL_SetAtomicInt(&windowData->textureContainers[i].activeTexture->referenceCount, 0);
4681
4682 // Create slice
4683 windowData->textureContainers[i].activeTexture->subresourceCount = 1;
4684 windowData->textureContainers[i].activeTexture->subresources = SDL_malloc(sizeof(VulkanTextureSubresource));
4685 windowData->textureContainers[i].activeTexture->subresources[0].parent = windowData->textureContainers[i].activeTexture;
4686 windowData->textureContainers[i].activeTexture->subresources[0].layer = 0;
4687 windowData->textureContainers[i].activeTexture->subresources[0].level = 0;
4688 windowData->textureContainers[i].activeTexture->subresources[0].renderTargetViews = SDL_malloc(sizeof(VkImageView));
4689 if (!VULKAN_INTERNAL_CreateRenderTargetView(
4690 renderer,
4691 windowData->textureContainers[i].activeTexture,
4692 0,
4693 0,
4694 windowData->format,
4695 windowData->swapchainSwizzle,
4696 &windowData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0])) {
4697 renderer->vkDestroySurfaceKHR(
4698 renderer->instance,
4699 windowData->surface,
4700 NULL);
4701 renderer->vkDestroySwapchainKHR(
4702 renderer->logicalDevice,
4703 windowData->swapchain,
4704 NULL);
4705 windowData->surface = VK_NULL_HANDLE;
4706 windowData->swapchain = VK_NULL_HANDLE;
4707 return false;
4708 }
4709 }
4710
4711 SDL_stack_free(swapchainImages);
4712
4713 semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
4714 semaphoreCreateInfo.pNext = NULL;
4715 semaphoreCreateInfo.flags = 0;
4716
4717 for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
4718 vulkanResult = renderer->vkCreateSemaphore(
4719 renderer->logicalDevice,
4720 &semaphoreCreateInfo,
4721 NULL,
4722 &windowData->imageAvailableSemaphore[i]);
4723
4724 if (vulkanResult != VK_SUCCESS) {
4725 renderer->vkDestroySurfaceKHR(
4726 renderer->instance,
4727 windowData->surface,
4728 NULL);
4729 renderer->vkDestroySwapchainKHR(
4730 renderer->logicalDevice,
4731 windowData->swapchain,
4732 NULL);
4733 windowData->surface = VK_NULL_HANDLE;
4734 windowData->swapchain = VK_NULL_HANDLE;
4735 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSemaphore, false);
4736 }
4737
4738 vulkanResult = renderer->vkCreateSemaphore(
4739 renderer->logicalDevice,
4740 &semaphoreCreateInfo,
4741 NULL,
4742 &windowData->renderFinishedSemaphore[i]);
4743
4744 if (vulkanResult != VK_SUCCESS) {
4745 renderer->vkDestroySurfaceKHR(
4746 renderer->instance,
4747 windowData->surface,
4748 NULL);
4749 renderer->vkDestroySwapchainKHR(
4750 renderer->logicalDevice,
4751 windowData->swapchain,
4752 NULL);
4753 windowData->surface = VK_NULL_HANDLE;
4754 windowData->swapchain = VK_NULL_HANDLE;
4755 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSemaphore, false);
4756 }
4757
4758 windowData->inFlightFences[i] = NULL;
4759 }
4760
4761 windowData->needsSwapchainRecreate = false;
4762 return true;
4763}
4764
4765// Command Buffers
4766
4767static bool VULKAN_INTERNAL_BeginCommandBuffer(
4768 VulkanRenderer *renderer,
4769 VulkanCommandBuffer *commandBuffer)
4770{
4771 VkCommandBufferBeginInfo beginInfo;
4772 VkResult result;
4773
4774 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
4775 beginInfo.pNext = NULL;
4776 beginInfo.flags = 0;
4777 beginInfo.pInheritanceInfo = NULL;
4778 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
4779
4780 result = renderer->vkBeginCommandBuffer(
4781 commandBuffer->commandBuffer,
4782 &beginInfo);
4783
4784 CHECK_VULKAN_ERROR_AND_RETURN(result, vkBeginCommandBuffer, false);
4785
4786 return true;
4787}
4788
4789static bool VULKAN_INTERNAL_EndCommandBuffer(
4790 VulkanRenderer *renderer,
4791 VulkanCommandBuffer *commandBuffer)
4792{
4793 VkResult result = renderer->vkEndCommandBuffer(
4794 commandBuffer->commandBuffer);
4795
4796 CHECK_VULKAN_ERROR_AND_RETURN(result, vkEndCommandBuffer, false);
4797
4798 return true;
4799}
4800
4801static void VULKAN_DestroyDevice(
4802 SDL_GPUDevice *device)
4803{
4804 VulkanRenderer *renderer = (VulkanRenderer *)device->driverData;
4805 VulkanMemorySubAllocator *allocator;
4806
4807 VULKAN_Wait(device->driverData);
4808
4809 for (Sint32 i = renderer->claimedWindowCount - 1; i >= 0; i -= 1) {
4810 VULKAN_ReleaseWindow(device->driverData, renderer->claimedWindows[i]->window);
4811 }
4812
4813 SDL_free(renderer->claimedWindows);
4814
4815 VULKAN_Wait(device->driverData);
4816
4817 SDL_free(renderer->submittedCommandBuffers);
4818
4819 for (Uint32 i = 0; i < renderer->uniformBufferPoolCount; i += 1) {
4820 VULKAN_INTERNAL_DestroyBuffer(
4821 renderer,
4822 renderer->uniformBufferPool[i]->buffer);
4823 SDL_free(renderer->uniformBufferPool[i]);
4824 }
4825 SDL_free(renderer->uniformBufferPool);
4826
4827 for (Uint32 i = 0; i < renderer->descriptorSetCachePoolCount; i += 1) {
4828 VULKAN_INTERNAL_DestroyDescriptorSetCache(
4829 renderer,
4830 renderer->descriptorSetCachePool[i]);
4831 }
4832 SDL_free(renderer->descriptorSetCachePool);
4833
4834 for (Uint32 i = 0; i < renderer->fencePool.availableFenceCount; i += 1) {
4835 renderer->vkDestroyFence(
4836 renderer->logicalDevice,
4837 renderer->fencePool.availableFences[i]->fence,
4838 NULL);
4839
4840 SDL_free(renderer->fencePool.availableFences[i]);
4841 }
4842
4843 SDL_free(renderer->fencePool.availableFences);
4844 SDL_DestroyMutex(renderer->fencePool.lock);
4845
4846 SDL_DestroyHashTable(renderer->commandPoolHashTable);
4847 SDL_DestroyHashTable(renderer->renderPassHashTable);
4848 SDL_DestroyHashTable(renderer->framebufferHashTable);
4849 SDL_DestroyHashTable(renderer->graphicsPipelineResourceLayoutHashTable);
4850 SDL_DestroyHashTable(renderer->computePipelineResourceLayoutHashTable);
4851 SDL_DestroyHashTable(renderer->descriptorSetLayoutHashTable);
4852
4853 for (Uint32 i = 0; i < VK_MAX_MEMORY_TYPES; i += 1) {
4854 allocator = &renderer->memoryAllocator->subAllocators[i];
4855
4856 for (Sint32 j = allocator->allocationCount - 1; j >= 0; j -= 1) {
4857 for (Sint32 k = allocator->allocations[j]->usedRegionCount - 1; k >= 0; k -= 1) {
4858 VULKAN_INTERNAL_RemoveMemoryUsedRegion(
4859 renderer,
4860 allocator->allocations[j]->usedRegions[k]);
4861 }
4862
4863 VULKAN_INTERNAL_DeallocateMemory(
4864 renderer,
4865 allocator,
4866 j);
4867 }
4868
4869 if (renderer->memoryAllocator->subAllocators[i].allocations != NULL) {
4870 SDL_free(renderer->memoryAllocator->subAllocators[i].allocations);
4871 }
4872
4873 SDL_free(renderer->memoryAllocator->subAllocators[i].sortedFreeRegions);
4874 }
4875
4876 SDL_free(renderer->memoryAllocator);
4877
4878 SDL_free(renderer->texturesToDestroy);
4879 SDL_free(renderer->buffersToDestroy);
4880 SDL_free(renderer->graphicsPipelinesToDestroy);
4881 SDL_free(renderer->computePipelinesToDestroy);
4882 SDL_free(renderer->shadersToDestroy);
4883 SDL_free(renderer->samplersToDestroy);
4884 SDL_free(renderer->framebuffersToDestroy);
4885 SDL_free(renderer->allocationsToDefrag);
4886
4887 SDL_DestroyMutex(renderer->allocatorLock);
4888 SDL_DestroyMutex(renderer->disposeLock);
4889 SDL_DestroyMutex(renderer->submitLock);
4890 SDL_DestroyMutex(renderer->acquireCommandBufferLock);
4891 SDL_DestroyMutex(renderer->acquireUniformBufferLock);
4892 SDL_DestroyMutex(renderer->framebufferFetchLock);
4893 SDL_DestroyMutex(renderer->windowLock);
4894
4895 renderer->vkDestroyDevice(renderer->logicalDevice, NULL);
4896 renderer->vkDestroyInstance(renderer->instance, NULL);
4897
4898 SDL_free(renderer);
4899 SDL_free(device);
4900 SDL_Vulkan_UnloadLibrary();
4901}
4902
4903static DescriptorSetCache *VULKAN_INTERNAL_AcquireDescriptorSetCache(
4904 VulkanRenderer *renderer)
4905{
4906 DescriptorSetCache *cache;
4907
4908 if (renderer->descriptorSetCachePoolCount == 0) {
4909 cache = SDL_malloc(sizeof(DescriptorSetCache));
4910 cache->poolCount = 0;
4911 cache->pools = NULL;
4912 } else {
4913 cache = renderer->descriptorSetCachePool[renderer->descriptorSetCachePoolCount - 1];
4914 renderer->descriptorSetCachePoolCount -= 1;
4915 }
4916
4917 return cache;
4918}
4919
4920static void VULKAN_INTERNAL_ReturnDescriptorSetCacheToPool(
4921 VulkanRenderer *renderer,
4922 DescriptorSetCache *descriptorSetCache)
4923{
4924 EXPAND_ARRAY_IF_NEEDED(
4925 renderer->descriptorSetCachePool,
4926 DescriptorSetCache *,
4927 renderer->descriptorSetCachePoolCount + 1,
4928 renderer->descriptorSetCachePoolCapacity,
4929 renderer->descriptorSetCachePoolCapacity * 2);
4930
4931 renderer->descriptorSetCachePool[renderer->descriptorSetCachePoolCount] = descriptorSetCache;
4932 renderer->descriptorSetCachePoolCount += 1;
4933
4934 for (Uint32 i = 0; i < descriptorSetCache->poolCount; i += 1) {
4935 descriptorSetCache->pools[i].descriptorSetIndex = 0;
4936 }
4937}
4938
4939static VkDescriptorSet VULKAN_INTERNAL_FetchDescriptorSet(
4940 VulkanRenderer *renderer,
4941 VulkanCommandBuffer *vulkanCommandBuffer,
4942 DescriptorSetLayout *descriptorSetLayout)
4943{
4944 // Grow the pool to meet the descriptor set layout ID
4945 if (descriptorSetLayout->ID >= vulkanCommandBuffer->descriptorSetCache->poolCount) {
4946 vulkanCommandBuffer->descriptorSetCache->pools = SDL_realloc(
4947 vulkanCommandBuffer->descriptorSetCache->pools,
4948 sizeof(DescriptorSetPool) * (descriptorSetLayout->ID + 1));
4949
4950 for (Uint32 i = vulkanCommandBuffer->descriptorSetCache->poolCount; i < descriptorSetLayout->ID + 1; i += 1) {
4951 SDL_zero(vulkanCommandBuffer->descriptorSetCache->pools[i]);
4952 }
4953
4954 vulkanCommandBuffer->descriptorSetCache->poolCount = descriptorSetLayout->ID + 1;
4955 }
4956
4957 DescriptorSetPool *pool =
4958 &vulkanCommandBuffer->descriptorSetCache->pools[descriptorSetLayout->ID];
4959
4960 if (pool->descriptorSetIndex == pool->descriptorSetCount) {
4961 if (!VULKAN_INTERNAL_AllocateDescriptorsFromPool(
4962 renderer,
4963 descriptorSetLayout,
4964 pool)) {
4965 return VK_NULL_HANDLE;
4966 }
4967 }
4968
4969 VkDescriptorSet descriptorSet = pool->descriptorSets[pool->descriptorSetIndex];
4970 pool->descriptorSetIndex += 1;
4971
4972 return descriptorSet;
4973}
4974
4975static void VULKAN_INTERNAL_BindGraphicsDescriptorSets(
4976 VulkanRenderer *renderer,
4977 VulkanCommandBuffer *commandBuffer)
4978{
4979 VulkanGraphicsPipelineResourceLayout *resourceLayout;
4980 DescriptorSetLayout *descriptorSetLayout;
4981 VkWriteDescriptorSet writeDescriptorSets[
4982 (MAX_TEXTURE_SAMPLERS_PER_STAGE +
4983 MAX_STORAGE_TEXTURES_PER_STAGE +
4984 MAX_STORAGE_BUFFERS_PER_STAGE +
4985 MAX_UNIFORM_BUFFERS_PER_STAGE) * 2];
4986 VkDescriptorBufferInfo bufferInfos[MAX_STORAGE_BUFFERS_PER_STAGE * 2];
4987 VkDescriptorImageInfo imageInfos[(MAX_TEXTURE_SAMPLERS_PER_STAGE + MAX_STORAGE_TEXTURES_PER_STAGE) * 2];
4988 Uint32 dynamicOffsets[MAX_UNIFORM_BUFFERS_PER_STAGE * 2];
4989 Uint32 writeCount = 0;
4990 Uint32 bufferInfoCount = 0;
4991 Uint32 imageInfoCount = 0;
4992 Uint32 dynamicOffsetCount = 0;
4993
4994 if (
4995 !commandBuffer->needVertexBufferBind &&
4996 !commandBuffer->needNewVertexResourceDescriptorSet &&
4997 !commandBuffer->needNewVertexUniformDescriptorSet &&
4998 !commandBuffer->needNewVertexUniformOffsets &&
4999 !commandBuffer->needNewFragmentResourceDescriptorSet &&
5000 !commandBuffer->needNewFragmentUniformDescriptorSet &&
5001 !commandBuffer->needNewFragmentUniformOffsets
5002 ) {
5003 return;
5004 }
5005
5006 if (commandBuffer->needVertexBufferBind && commandBuffer->vertexBufferCount > 0) {
5007 renderer->vkCmdBindVertexBuffers(
5008 commandBuffer->commandBuffer,
5009 0,
5010 commandBuffer->vertexBufferCount,
5011 commandBuffer->vertexBuffers,
5012 commandBuffer->vertexBufferOffsets);
5013
5014 commandBuffer->needVertexBufferBind = false;
5015 }
5016
5017 resourceLayout = commandBuffer->currentGraphicsPipeline->resourceLayout;
5018
5019 if (commandBuffer->needNewVertexResourceDescriptorSet) {
5020 descriptorSetLayout = resourceLayout->descriptorSetLayouts[0];
5021
5022 commandBuffer->vertexResourceDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet(
5023 renderer,
5024 commandBuffer,
5025 descriptorSetLayout);
5026
5027 for (Uint32 i = 0; i < resourceLayout->vertexSamplerCount; i += 1) {
5028 VkWriteDescriptorSet *currentWriteDescriptorSet = &writeDescriptorSets[writeCount];
5029
5030 currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
5031 currentWriteDescriptorSet->pNext = NULL;
5032 currentWriteDescriptorSet->descriptorCount = 1;
5033 currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
5034 currentWriteDescriptorSet->dstArrayElement = 0;
5035 currentWriteDescriptorSet->dstBinding = i;
5036 currentWriteDescriptorSet->dstSet = commandBuffer->vertexResourceDescriptorSet;
5037 currentWriteDescriptorSet->pTexelBufferView = NULL;
5038 currentWriteDescriptorSet->pBufferInfo = NULL;
5039
5040 imageInfos[imageInfoCount].sampler = commandBuffer->vertexSamplers[i]->sampler;
5041 imageInfos[imageInfoCount].imageView = commandBuffer->vertexSamplerTextures[i]->fullView;
5042 imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
5043
5044 currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount];
5045
5046 writeCount += 1;
5047 imageInfoCount += 1;
5048 }
5049
5050 for (Uint32 i = 0; i < resourceLayout->vertexStorageTextureCount; i += 1) {
5051 VkWriteDescriptorSet *currentWriteDescriptorSet = &writeDescriptorSets[writeCount];
5052
5053 currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
5054 currentWriteDescriptorSet->pNext = NULL;
5055 currentWriteDescriptorSet->descriptorCount = 1;
5056 currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; // Yes, we are declaring a storage image as a sampled image, because shaders are stupid.
5057 currentWriteDescriptorSet->dstArrayElement = 0;
5058 currentWriteDescriptorSet->dstBinding = resourceLayout->vertexSamplerCount + i;
5059 currentWriteDescriptorSet->dstSet = commandBuffer->vertexResourceDescriptorSet;
5060 currentWriteDescriptorSet->pTexelBufferView = NULL;
5061 currentWriteDescriptorSet->pBufferInfo = NULL;
5062
5063 imageInfos[imageInfoCount].sampler = VK_NULL_HANDLE;
5064 imageInfos[imageInfoCount].imageView = commandBuffer->vertexStorageTextures[i]->fullView;
5065 imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
5066
5067 currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount];
5068
5069 writeCount += 1;
5070 imageInfoCount += 1;
5071 }
5072
5073 for (Uint32 i = 0; i < resourceLayout->vertexStorageBufferCount; i += 1) {
5074 VkWriteDescriptorSet *currentWriteDescriptorSet = &writeDescriptorSets[writeCount];
5075
5076 currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
5077 currentWriteDescriptorSet->pNext = NULL;
5078 currentWriteDescriptorSet->descriptorCount = 1;
5079 currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
5080 currentWriteDescriptorSet->dstArrayElement = 0;
5081 currentWriteDescriptorSet->dstBinding = resourceLayout->vertexSamplerCount + resourceLayout->vertexStorageTextureCount + i;
5082 currentWriteDescriptorSet->dstSet = commandBuffer->vertexResourceDescriptorSet;
5083 currentWriteDescriptorSet->pTexelBufferView = NULL;
5084 currentWriteDescriptorSet->pImageInfo = NULL;
5085
5086 bufferInfos[bufferInfoCount].buffer = commandBuffer->vertexStorageBuffers[i]->buffer;
5087 bufferInfos[bufferInfoCount].offset = 0;
5088 bufferInfos[bufferInfoCount].range = VK_WHOLE_SIZE;
5089
5090 currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount];
5091
5092 writeCount += 1;
5093 bufferInfoCount += 1;
5094 }
5095
5096 commandBuffer->needNewVertexResourceDescriptorSet = false;
5097 }
5098
5099 if (commandBuffer->needNewVertexUniformDescriptorSet) {
5100 descriptorSetLayout = resourceLayout->descriptorSetLayouts[1];
5101
5102 commandBuffer->vertexUniformDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet(
5103 renderer,
5104 commandBuffer,
5105 descriptorSetLayout);
5106
5107 for (Uint32 i = 0; i < resourceLayout->vertexUniformBufferCount; i += 1) {
5108 VkWriteDescriptorSet *currentWriteDescriptorSet = &writeDescriptorSets[writeCount];
5109
5110 currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
5111 currentWriteDescriptorSet->pNext = NULL;
5112 currentWriteDescriptorSet->descriptorCount = 1;
5113 currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
5114 currentWriteDescriptorSet->dstArrayElement = 0;
5115 currentWriteDescriptorSet->dstBinding = i;
5116 currentWriteDescriptorSet->dstSet = commandBuffer->vertexUniformDescriptorSet;
5117 currentWriteDescriptorSet->pTexelBufferView = NULL;
5118 currentWriteDescriptorSet->pImageInfo = NULL;
5119
5120 bufferInfos[bufferInfoCount].buffer = commandBuffer->vertexUniformBuffers[i]->buffer->buffer;
5121 bufferInfos[bufferInfoCount].offset = 0;
5122 bufferInfos[bufferInfoCount].range = MAX_UBO_SECTION_SIZE;
5123
5124 currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount];
5125
5126 writeCount += 1;
5127 bufferInfoCount += 1;
5128 }
5129
5130 commandBuffer->needNewVertexUniformDescriptorSet = false;
5131 }
5132
5133 for (Uint32 i = 0; i < resourceLayout->vertexUniformBufferCount; i += 1) {
5134 dynamicOffsets[dynamicOffsetCount] = commandBuffer->vertexUniformBuffers[i]->drawOffset;
5135 dynamicOffsetCount += 1;
5136 }
5137
5138 if (commandBuffer->needNewFragmentResourceDescriptorSet) {
5139 descriptorSetLayout = resourceLayout->descriptorSetLayouts[2];
5140
5141 commandBuffer->fragmentResourceDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet(
5142 renderer,
5143 commandBuffer,
5144 descriptorSetLayout);
5145
5146 for (Uint32 i = 0; i < resourceLayout->fragmentSamplerCount; i += 1) {
5147 VkWriteDescriptorSet *currentWriteDescriptorSet = &writeDescriptorSets[writeCount];
5148
5149 currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
5150 currentWriteDescriptorSet->pNext = NULL;
5151 currentWriteDescriptorSet->descriptorCount = 1;
5152 currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
5153 currentWriteDescriptorSet->dstArrayElement = 0;
5154 currentWriteDescriptorSet->dstBinding = i;
5155 currentWriteDescriptorSet->dstSet = commandBuffer->fragmentResourceDescriptorSet;
5156 currentWriteDescriptorSet->pTexelBufferView = NULL;
5157 currentWriteDescriptorSet->pBufferInfo = NULL;
5158
5159 imageInfos[imageInfoCount].sampler = commandBuffer->fragmentSamplers[i]->sampler;
5160 imageInfos[imageInfoCount].imageView = commandBuffer->fragmentSamplerTextures[i]->fullView;
5161 imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
5162
5163 currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount];
5164
5165 writeCount += 1;
5166 imageInfoCount += 1;
5167 }
5168
5169 for (Uint32 i = 0; i < resourceLayout->fragmentStorageTextureCount; i += 1) {
5170 VkWriteDescriptorSet *currentWriteDescriptorSet = &writeDescriptorSets[writeCount];
5171
5172 currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
5173 currentWriteDescriptorSet->pNext = NULL;
5174 currentWriteDescriptorSet->descriptorCount = 1;
5175 currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; // Yes, we are declaring a storage image as a sampled image, because shaders are stupid.
5176 currentWriteDescriptorSet->dstArrayElement = 0;
5177 currentWriteDescriptorSet->dstBinding = resourceLayout->fragmentSamplerCount + i;
5178 currentWriteDescriptorSet->dstSet = commandBuffer->fragmentResourceDescriptorSet;
5179 currentWriteDescriptorSet->pTexelBufferView = NULL;
5180 currentWriteDescriptorSet->pBufferInfo = NULL;
5181
5182 imageInfos[imageInfoCount].sampler = VK_NULL_HANDLE;
5183 imageInfos[imageInfoCount].imageView = commandBuffer->fragmentStorageTextures[i]->fullView;
5184 imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
5185
5186 currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount];
5187
5188 writeCount += 1;
5189 imageInfoCount += 1;
5190 }
5191
5192 for (Uint32 i = 0; i < resourceLayout->fragmentStorageBufferCount; i += 1) {
5193 VkWriteDescriptorSet *currentWriteDescriptorSet = &writeDescriptorSets[writeCount];
5194
5195 currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
5196 currentWriteDescriptorSet->pNext = NULL;
5197 currentWriteDescriptorSet->descriptorCount = 1;
5198 currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
5199 currentWriteDescriptorSet->dstArrayElement = 0;
5200 currentWriteDescriptorSet->dstBinding = resourceLayout->fragmentSamplerCount + resourceLayout->fragmentStorageTextureCount + i;
5201 currentWriteDescriptorSet->dstSet = commandBuffer->fragmentResourceDescriptorSet;
5202 currentWriteDescriptorSet->pTexelBufferView = NULL;
5203 currentWriteDescriptorSet->pImageInfo = NULL;
5204
5205 bufferInfos[bufferInfoCount].buffer = commandBuffer->fragmentStorageBuffers[i]->buffer;
5206 bufferInfos[bufferInfoCount].offset = 0;
5207 bufferInfos[bufferInfoCount].range = VK_WHOLE_SIZE;
5208
5209 currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount];
5210
5211 writeCount += 1;
5212 bufferInfoCount += 1;
5213 }
5214
5215 commandBuffer->needNewFragmentResourceDescriptorSet = false;
5216 }
5217
5218 if (commandBuffer->needNewFragmentUniformDescriptorSet) {
5219 descriptorSetLayout = resourceLayout->descriptorSetLayouts[3];
5220
5221 commandBuffer->fragmentUniformDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet(
5222 renderer,
5223 commandBuffer,
5224 descriptorSetLayout);
5225
5226 for (Uint32 i = 0; i < resourceLayout->fragmentUniformBufferCount; i += 1) {
5227 VkWriteDescriptorSet *currentWriteDescriptorSet = &writeDescriptorSets[writeCount];
5228
5229 currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
5230 currentWriteDescriptorSet->pNext = NULL;
5231 currentWriteDescriptorSet->descriptorCount = 1;
5232 currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
5233 currentWriteDescriptorSet->dstArrayElement = 0;
5234 currentWriteDescriptorSet->dstBinding = i;
5235 currentWriteDescriptorSet->dstSet = commandBuffer->fragmentUniformDescriptorSet;
5236 currentWriteDescriptorSet->pTexelBufferView = NULL;
5237 currentWriteDescriptorSet->pImageInfo = NULL;
5238
5239 bufferInfos[bufferInfoCount].buffer = commandBuffer->fragmentUniformBuffers[i]->buffer->buffer;
5240 bufferInfos[bufferInfoCount].offset = 0;
5241 bufferInfos[bufferInfoCount].range = MAX_UBO_SECTION_SIZE;
5242
5243 currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount];
5244
5245 writeCount += 1;
5246 bufferInfoCount += 1;
5247 }
5248
5249 commandBuffer->needNewFragmentUniformDescriptorSet = false;
5250 }
5251
5252 for (Uint32 i = 0; i < resourceLayout->fragmentUniformBufferCount; i += 1) {
5253 dynamicOffsets[dynamicOffsetCount] = commandBuffer->fragmentUniformBuffers[i]->drawOffset;
5254 dynamicOffsetCount += 1;
5255 }
5256
5257 renderer->vkUpdateDescriptorSets(
5258 renderer->logicalDevice,
5259 writeCount,
5260 writeDescriptorSets,
5261 0,
5262 NULL);
5263
5264 VkDescriptorSet sets[4];
5265 sets[0] = commandBuffer->vertexResourceDescriptorSet;
5266 sets[1] = commandBuffer->vertexUniformDescriptorSet;
5267 sets[2] = commandBuffer->fragmentResourceDescriptorSet;
5268 sets[3] = commandBuffer->fragmentUniformDescriptorSet;
5269
5270 renderer->vkCmdBindDescriptorSets(
5271 commandBuffer->commandBuffer,
5272 VK_PIPELINE_BIND_POINT_GRAPHICS,
5273 resourceLayout->pipelineLayout,
5274 0,
5275 4,
5276 sets,
5277 dynamicOffsetCount,
5278 dynamicOffsets);
5279
5280 commandBuffer->needNewVertexUniformOffsets = false;
5281 commandBuffer->needNewFragmentUniformOffsets = false;
5282}
5283
5284static void VULKAN_DrawIndexedPrimitives(
5285 SDL_GPUCommandBuffer *commandBuffer,
5286 Uint32 numIndices,
5287 Uint32 numInstances,
5288 Uint32 firstIndex,
5289 Sint32 vertexOffset,
5290 Uint32 firstInstance)
5291{
5292 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
5293 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
5294
5295 VULKAN_INTERNAL_BindGraphicsDescriptorSets(renderer, vulkanCommandBuffer);
5296
5297 renderer->vkCmdDrawIndexed(
5298 vulkanCommandBuffer->commandBuffer,
5299 numIndices,
5300 numInstances,
5301 firstIndex,
5302 vertexOffset,
5303 firstInstance);
5304}
5305
5306static void VULKAN_DrawPrimitives(
5307 SDL_GPUCommandBuffer *commandBuffer,
5308 Uint32 numVertices,
5309 Uint32 numInstances,
5310 Uint32 firstVertex,
5311 Uint32 firstInstance)
5312{
5313 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
5314 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
5315
5316 VULKAN_INTERNAL_BindGraphicsDescriptorSets(renderer, vulkanCommandBuffer);
5317
5318 renderer->vkCmdDraw(
5319 vulkanCommandBuffer->commandBuffer,
5320 numVertices,
5321 numInstances,
5322 firstVertex,
5323 firstInstance);
5324}
5325
5326static void VULKAN_DrawPrimitivesIndirect(
5327 SDL_GPUCommandBuffer *commandBuffer,
5328 SDL_GPUBuffer *buffer,
5329 Uint32 offset,
5330 Uint32 drawCount)
5331{
5332 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
5333 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
5334 VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer *)buffer)->activeBuffer;
5335 Uint32 pitch = sizeof(SDL_GPUIndirectDrawCommand);
5336 Uint32 i;
5337
5338 VULKAN_INTERNAL_BindGraphicsDescriptorSets(renderer, vulkanCommandBuffer);
5339
5340 if (renderer->supportsMultiDrawIndirect) {
5341 // Real multi-draw!
5342 renderer->vkCmdDrawIndirect(
5343 vulkanCommandBuffer->commandBuffer,
5344 vulkanBuffer->buffer,
5345 offset,
5346 drawCount,
5347 pitch);
5348 } else {
5349 // Fake multi-draw...
5350 for (i = 0; i < drawCount; i += 1) {
5351 renderer->vkCmdDrawIndirect(
5352 vulkanCommandBuffer->commandBuffer,
5353 vulkanBuffer->buffer,
5354 offset + (pitch * i),
5355 1,
5356 pitch);
5357 }
5358 }
5359
5360 VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, vulkanBuffer);
5361}
5362
5363static void VULKAN_DrawIndexedPrimitivesIndirect(
5364 SDL_GPUCommandBuffer *commandBuffer,
5365 SDL_GPUBuffer *buffer,
5366 Uint32 offset,
5367 Uint32 drawCount)
5368{
5369 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
5370 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
5371 VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer *)buffer)->activeBuffer;
5372 Uint32 pitch = sizeof(SDL_GPUIndexedIndirectDrawCommand);
5373 Uint32 i;
5374
5375 VULKAN_INTERNAL_BindGraphicsDescriptorSets(renderer, vulkanCommandBuffer);
5376
5377 if (renderer->supportsMultiDrawIndirect) {
5378 // Real multi-draw!
5379 renderer->vkCmdDrawIndexedIndirect(
5380 vulkanCommandBuffer->commandBuffer,
5381 vulkanBuffer->buffer,
5382 offset,
5383 drawCount,
5384 pitch);
5385 } else {
5386 // Fake multi-draw...
5387 for (i = 0; i < drawCount; i += 1) {
5388 renderer->vkCmdDrawIndexedIndirect(
5389 vulkanCommandBuffer->commandBuffer,
5390 vulkanBuffer->buffer,
5391 offset + (pitch * i),
5392 1,
5393 pitch);
5394 }
5395 }
5396
5397 VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, vulkanBuffer);
5398}
5399
5400// Debug Naming
5401
5402static void VULKAN_INTERNAL_SetBufferName(
5403 VulkanRenderer *renderer,
5404 VulkanBuffer *buffer,
5405 const char *text)
5406{
5407 VkDebugUtilsObjectNameInfoEXT nameInfo;
5408
5409 if (renderer->debugMode && renderer->supportsDebugUtils) {
5410 nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
5411 nameInfo.pNext = NULL;
5412 nameInfo.pObjectName = text;
5413 nameInfo.objectType = VK_OBJECT_TYPE_BUFFER;
5414 nameInfo.objectHandle = (uint64_t)buffer->buffer;
5415
5416 renderer->vkSetDebugUtilsObjectNameEXT(
5417 renderer->logicalDevice,
5418 &nameInfo);
5419 }
5420}
5421
5422static void VULKAN_SetBufferName(
5423 SDL_GPURenderer *driverData,
5424 SDL_GPUBuffer *buffer,
5425 const char *text)
5426{
5427 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
5428 VulkanBufferContainer *container = (VulkanBufferContainer *)buffer;
5429 size_t textLength = SDL_strlen(text) + 1;
5430
5431 if (renderer->debugMode && renderer->supportsDebugUtils) {
5432 container->debugName = SDL_realloc(
5433 container->debugName,
5434 textLength);
5435
5436 SDL_utf8strlcpy(
5437 container->debugName,
5438 text,
5439 textLength);
5440
5441 for (Uint32 i = 0; i < container->bufferCount; i += 1) {
5442 VULKAN_INTERNAL_SetBufferName(
5443 renderer,
5444 container->buffers[i],
5445 text);
5446 }
5447 }
5448}
5449
5450static void VULKAN_INTERNAL_SetTextureName(
5451 VulkanRenderer *renderer,
5452 VulkanTexture *texture,
5453 const char *text)
5454{
5455 VkDebugUtilsObjectNameInfoEXT nameInfo;
5456
5457 if (renderer->debugMode && renderer->supportsDebugUtils) {
5458 nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
5459 nameInfo.pNext = NULL;
5460 nameInfo.pObjectName = text;
5461 nameInfo.objectType = VK_OBJECT_TYPE_IMAGE;
5462 nameInfo.objectHandle = (uint64_t)texture->image;
5463
5464 renderer->vkSetDebugUtilsObjectNameEXT(
5465 renderer->logicalDevice,
5466 &nameInfo);
5467 }
5468}
5469
5470static void VULKAN_SetTextureName(
5471 SDL_GPURenderer *driverData,
5472 SDL_GPUTexture *texture,
5473 const char *text)
5474{
5475 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
5476 VulkanTextureContainer *container = (VulkanTextureContainer *)texture;
5477 size_t textLength = SDL_strlen(text) + 1;
5478
5479 if (renderer->debugMode && renderer->supportsDebugUtils) {
5480 container->debugName = SDL_realloc(
5481 container->debugName,
5482 textLength);
5483
5484 SDL_utf8strlcpy(
5485 container->debugName,
5486 text,
5487 textLength);
5488
5489 for (Uint32 i = 0; i < container->textureCount; i += 1) {
5490 VULKAN_INTERNAL_SetTextureName(
5491 renderer,
5492 container->textures[i],
5493 text);
5494 }
5495 }
5496}
5497
5498static void VULKAN_InsertDebugLabel(
5499 SDL_GPUCommandBuffer *commandBuffer,
5500 const char *text)
5501{
5502 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
5503 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
5504 VkDebugUtilsLabelEXT labelInfo;
5505
5506 if (renderer->supportsDebugUtils) {
5507 SDL_zero(labelInfo);
5508 labelInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
5509 labelInfo.pLabelName = text;
5510
5511 renderer->vkCmdInsertDebugUtilsLabelEXT(
5512 vulkanCommandBuffer->commandBuffer,
5513 &labelInfo);
5514 }
5515}
5516
5517static void VULKAN_PushDebugGroup(
5518 SDL_GPUCommandBuffer *commandBuffer,
5519 const char *name)
5520{
5521 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
5522 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
5523 VkDebugUtilsLabelEXT labelInfo;
5524
5525 if (renderer->supportsDebugUtils) {
5526 SDL_zero(labelInfo);
5527 labelInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
5528 labelInfo.pLabelName = name;
5529
5530 renderer->vkCmdBeginDebugUtilsLabelEXT(
5531 vulkanCommandBuffer->commandBuffer,
5532 &labelInfo);
5533 }
5534}
5535
5536static void VULKAN_PopDebugGroup(
5537 SDL_GPUCommandBuffer *commandBuffer)
5538{
5539 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
5540 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
5541
5542 if (renderer->supportsDebugUtils) {
5543 renderer->vkCmdEndDebugUtilsLabelEXT(vulkanCommandBuffer->commandBuffer);
5544 }
5545}
5546
5547static VulkanTexture *VULKAN_INTERNAL_CreateTexture(
5548 VulkanRenderer *renderer,
5549 const SDL_GPUTextureCreateInfo *createinfo)
5550{
5551 VkResult vulkanResult;
5552 VkImageCreateInfo imageCreateInfo;
5553 VkImageCreateFlags imageCreateFlags = 0;
5554 VkImageViewCreateInfo imageViewCreateInfo;
5555 Uint8 bindResult;
5556 VkImageUsageFlags vkUsageFlags = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
5557 Uint32 layerCount = (createinfo->type == SDL_GPU_TEXTURETYPE_3D) ? 1 : createinfo->layer_count_or_depth;
5558 Uint32 depth = (createinfo->type == SDL_GPU_TEXTURETYPE_3D) ? createinfo->layer_count_or_depth : 1;
5559
5560 VulkanTexture *texture = SDL_calloc(1, sizeof(VulkanTexture));
5561 texture->swizzle = SwizzleForSDLFormat(createinfo->format);
5562 texture->depth = depth;
5563 texture->usage = createinfo->usage;
5564 SDL_SetAtomicInt(&texture->referenceCount, 0);
5565
5566 if (IsDepthFormat(createinfo->format)) {
5567 texture->aspectFlags = VK_IMAGE_ASPECT_DEPTH_BIT;
5568
5569 if (IsStencilFormat(createinfo->format)) {
5570 texture->aspectFlags |= VK_IMAGE_ASPECT_STENCIL_BIT;
5571 }
5572 } else {
5573 texture->aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
5574 }
5575
5576 if (createinfo->type == SDL_GPU_TEXTURETYPE_CUBE || createinfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) {
5577 imageCreateFlags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
5578 } else if (createinfo->type == SDL_GPU_TEXTURETYPE_3D) {
5579 imageCreateFlags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
5580 }
5581
5582 if (createinfo->usage & (SDL_GPU_TEXTUREUSAGE_SAMPLER |
5583 SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ |
5584 SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ)) {
5585 vkUsageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT;
5586 }
5587 if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
5588 vkUsageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
5589 }
5590 if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
5591 vkUsageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
5592 }
5593 if (createinfo->usage & (SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE |
5594 SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE)) {
5595 vkUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT;
5596 }
5597
5598 imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
5599 imageCreateInfo.pNext = NULL;
5600 imageCreateInfo.flags = imageCreateFlags;
5601 imageCreateInfo.imageType = createinfo->type == SDL_GPU_TEXTURETYPE_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D;
5602 imageCreateInfo.format = SDLToVK_TextureFormat[createinfo->format];
5603 imageCreateInfo.extent.width = createinfo->width;
5604 imageCreateInfo.extent.height = createinfo->height;
5605 imageCreateInfo.extent.depth = depth;
5606 imageCreateInfo.mipLevels = createinfo->num_levels;
5607 imageCreateInfo.arrayLayers = layerCount;
5608 imageCreateInfo.samples = SDLToVK_SampleCount[createinfo->sample_count];
5609 imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
5610 imageCreateInfo.usage = vkUsageFlags;
5611 imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
5612 imageCreateInfo.queueFamilyIndexCount = 0;
5613 imageCreateInfo.pQueueFamilyIndices = NULL;
5614 imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
5615
5616 vulkanResult = renderer->vkCreateImage(
5617 renderer->logicalDevice,
5618 &imageCreateInfo,
5619 NULL,
5620 &texture->image);
5621
5622 if (vulkanResult != VK_SUCCESS) {
5623 VULKAN_INTERNAL_DestroyTexture(renderer, texture);
5624 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateImage, NULL);
5625 }
5626
5627 bindResult = VULKAN_INTERNAL_BindMemoryForImage(
5628 renderer,
5629 texture->image,
5630 &texture->usedRegion);
5631
5632 if (bindResult != 1) {
5633 renderer->vkDestroyImage(
5634 renderer->logicalDevice,
5635 texture->image,
5636 NULL);
5637
5638 VULKAN_INTERNAL_DestroyTexture(renderer, texture);
5639 SET_STRING_ERROR_AND_RETURN("Unable to bind memory for texture!", NULL);
5640 }
5641
5642 texture->usedRegion->vulkanTexture = texture; // lol
5643
5644 if (createinfo->usage & (SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ)) {
5645
5646 imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
5647 imageViewCreateInfo.pNext = NULL;
5648 imageViewCreateInfo.flags = 0;
5649 imageViewCreateInfo.image = texture->image;
5650 imageViewCreateInfo.format = SDLToVK_TextureFormat[createinfo->format];
5651 imageViewCreateInfo.components = texture->swizzle;
5652 imageViewCreateInfo.subresourceRange.aspectMask = texture->aspectFlags & ~VK_IMAGE_ASPECT_STENCIL_BIT; // Can't sample stencil values
5653 imageViewCreateInfo.subresourceRange.baseMipLevel = 0;
5654 imageViewCreateInfo.subresourceRange.levelCount = createinfo->num_levels;
5655 imageViewCreateInfo.subresourceRange.baseArrayLayer = 0;
5656 imageViewCreateInfo.subresourceRange.layerCount = layerCount;
5657
5658 if (createinfo->type == SDL_GPU_TEXTURETYPE_CUBE) {
5659 imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
5660 } else if (createinfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) {
5661 imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
5662 } else if (createinfo->type == SDL_GPU_TEXTURETYPE_3D) {
5663 imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_3D;
5664 } else if (createinfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY) {
5665 imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
5666 } else {
5667 imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
5668 }
5669
5670 vulkanResult = renderer->vkCreateImageView(
5671 renderer->logicalDevice,
5672 &imageViewCreateInfo,
5673 NULL,
5674 &texture->fullView);
5675
5676 if (vulkanResult != VK_SUCCESS) {
5677 VULKAN_INTERNAL_DestroyTexture(renderer, texture);
5678 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, "vkCreateImageView", NULL);
5679 }
5680 }
5681
5682 // Define slices
5683 texture->subresourceCount = layerCount * createinfo->num_levels;
5684 texture->subresources = SDL_calloc(
5685 texture->subresourceCount,
5686 sizeof(VulkanTextureSubresource));
5687
5688 for (Uint32 i = 0; i < layerCount; i += 1) {
5689 for (Uint32 j = 0; j < createinfo->num_levels; j += 1) {
5690 Uint32 subresourceIndex = VULKAN_INTERNAL_GetTextureSubresourceIndex(
5691 j,
5692 i,
5693 createinfo->num_levels);
5694
5695 if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
5696 texture->subresources[subresourceIndex].renderTargetViews = SDL_malloc(
5697 depth * sizeof(VkImageView));
5698
5699 if (depth > 1) {
5700 for (Uint32 k = 0; k < depth; k += 1) {
5701 if (!VULKAN_INTERNAL_CreateRenderTargetView(
5702 renderer,
5703 texture,
5704 k,
5705 j,
5706 SDLToVK_TextureFormat[createinfo->format],
5707 texture->swizzle,
5708 &texture->subresources[subresourceIndex].renderTargetViews[k])) {
5709 VULKAN_INTERNAL_DestroyTexture(renderer, texture);
5710 return NULL;
5711 }
5712 }
5713 } else {
5714 if (!VULKAN_INTERNAL_CreateRenderTargetView(
5715 renderer,
5716 texture,
5717 i,
5718 j,
5719 SDLToVK_TextureFormat[createinfo->format],
5720 texture->swizzle,
5721 &texture->subresources[subresourceIndex].renderTargetViews[0])) {
5722 VULKAN_INTERNAL_DestroyTexture(renderer, texture);
5723 return NULL;
5724 }
5725 }
5726 }
5727
5728 if ((createinfo->usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) || (createinfo->usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE)) {
5729 if (!VULKAN_INTERNAL_CreateSubresourceView(
5730 renderer,
5731 createinfo,
5732 texture,
5733 i,
5734 j,
5735 texture->swizzle,
5736 &texture->subresources[subresourceIndex].computeWriteView)) {
5737 VULKAN_INTERNAL_DestroyTexture(renderer, texture);
5738 return NULL;
5739 }
5740 }
5741
5742 if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
5743 if (!VULKAN_INTERNAL_CreateSubresourceView(
5744 renderer,
5745 createinfo,
5746 texture,
5747 i,
5748 j,
5749 texture->swizzle,
5750 &texture->subresources[subresourceIndex].depthStencilView)) {
5751 VULKAN_INTERNAL_DestroyTexture(renderer, texture);
5752 return NULL;
5753 }
5754 }
5755
5756 texture->subresources[subresourceIndex].parent = texture;
5757 texture->subresources[subresourceIndex].layer = i;
5758 texture->subresources[subresourceIndex].level = j;
5759 }
5760 }
5761
5762 // Set debug name if applicable
5763 if (renderer->debugMode && renderer->supportsDebugUtils && SDL_HasProperty(createinfo->props, SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING)) {
5764 VkDebugUtilsObjectNameInfoEXT nameInfo;
5765 nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
5766 nameInfo.pNext = NULL;
5767 nameInfo.pObjectName = SDL_GetStringProperty(createinfo->props, SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING, NULL);
5768 nameInfo.objectType = VK_OBJECT_TYPE_IMAGE;
5769 nameInfo.objectHandle = (uint64_t)texture->image;
5770
5771 renderer->vkSetDebugUtilsObjectNameEXT(
5772 renderer->logicalDevice,
5773 &nameInfo);
5774 }
5775
5776 // Let's transition to the default barrier state, because for some reason Vulkan doesn't let us do that with initialLayout.
5777 VulkanCommandBuffer *barrierCommandBuffer = (VulkanCommandBuffer *)VULKAN_AcquireCommandBuffer((SDL_GPURenderer *)renderer);
5778 VULKAN_INTERNAL_TextureTransitionToDefaultUsage(
5779 renderer,
5780 barrierCommandBuffer,
5781 VULKAN_TEXTURE_USAGE_MODE_UNINITIALIZED,
5782 texture);
5783 VULKAN_INTERNAL_TrackTexture(barrierCommandBuffer, texture);
5784 VULKAN_Submit((SDL_GPUCommandBuffer *)barrierCommandBuffer);
5785
5786 return texture;
5787}
5788
5789static void VULKAN_INTERNAL_CycleActiveBuffer(
5790 VulkanRenderer *renderer,
5791 VulkanBufferContainer *container)
5792{
5793 VulkanBuffer *buffer;
5794
5795 // If a previously-cycled buffer is available, we can use that.
5796 for (Uint32 i = 0; i < container->bufferCount; i += 1) {
5797 buffer = container->buffers[i];
5798 if (SDL_GetAtomicInt(&buffer->referenceCount) == 0) {
5799 container->activeBuffer = buffer;
5800 return;
5801 }
5802 }
5803
5804 // No buffer handle is available, create a new one.
5805 buffer = VULKAN_INTERNAL_CreateBuffer(
5806 renderer,
5807 container->activeBuffer->size,
5808 container->activeBuffer->usage,
5809 container->activeBuffer->type,
5810 container->dedicated,
5811 container->debugName);
5812
5813 if (!buffer) {
5814 return;
5815 }
5816
5817 EXPAND_ARRAY_IF_NEEDED(
5818 container->buffers,
5819 VulkanBuffer *,
5820 container->bufferCount + 1,
5821 container->bufferCapacity,
5822 container->bufferCapacity * 2);
5823
5824 container->buffers[container->bufferCount] = buffer;
5825 buffer->container = container;
5826 buffer->containerIndex = container->bufferCount;
5827 container->bufferCount += 1;
5828
5829 container->activeBuffer = buffer;
5830}
5831
5832static void VULKAN_INTERNAL_CycleActiveTexture(
5833 VulkanRenderer *renderer,
5834 VulkanTextureContainer *container)
5835{
5836 VulkanTexture *texture;
5837
5838 // If a previously-cycled texture is available, we can use that.
5839 for (Uint32 i = 0; i < container->textureCount; i += 1) {
5840 texture = container->textures[i];
5841
5842 if (SDL_GetAtomicInt(&texture->referenceCount) == 0) {
5843 container->activeTexture = texture;
5844 return;
5845 }
5846 }
5847
5848 // No texture is available, generate a new one.
5849 texture = VULKAN_INTERNAL_CreateTexture(
5850 renderer,
5851 &container->header.info);
5852
5853 if (!texture) {
5854 return;
5855 }
5856
5857 EXPAND_ARRAY_IF_NEEDED(
5858 container->textures,
5859 VulkanTexture *,
5860 container->textureCount + 1,
5861 container->textureCapacity,
5862 container->textureCapacity * 2);
5863
5864 container->textures[container->textureCount] = texture;
5865 texture->container = container;
5866 texture->containerIndex = container->textureCount;
5867 container->textureCount += 1;
5868
5869 container->activeTexture = texture;
5870}
5871
5872static VulkanBuffer *VULKAN_INTERNAL_PrepareBufferForWrite(
5873 VulkanRenderer *renderer,
5874 VulkanCommandBuffer *commandBuffer,
5875 VulkanBufferContainer *bufferContainer,
5876 bool cycle,
5877 VulkanBufferUsageMode destinationUsageMode)
5878{
5879 if (
5880 cycle &&
5881 SDL_GetAtomicInt(&bufferContainer->activeBuffer->referenceCount) > 0) {
5882 VULKAN_INTERNAL_CycleActiveBuffer(
5883 renderer,
5884 bufferContainer);
5885 }
5886
5887 VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
5888 renderer,
5889 commandBuffer,
5890 destinationUsageMode,
5891 bufferContainer->activeBuffer);
5892
5893 return bufferContainer->activeBuffer;
5894}
5895
5896static VulkanTextureSubresource *VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
5897 VulkanRenderer *renderer,
5898 VulkanCommandBuffer *commandBuffer,
5899 VulkanTextureContainer *textureContainer,
5900 Uint32 layer,
5901 Uint32 level,
5902 bool cycle,
5903 VulkanTextureUsageMode destinationUsageMode)
5904{
5905 VulkanTextureSubresource *textureSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
5906 textureContainer,
5907 layer,
5908 level);
5909
5910 if (
5911 cycle &&
5912 textureContainer->canBeCycled &&
5913 SDL_GetAtomicInt(&textureContainer->activeTexture->referenceCount) > 0) {
5914 VULKAN_INTERNAL_CycleActiveTexture(
5915 renderer,
5916 textureContainer);
5917
5918 textureSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
5919 textureContainer,
5920 layer,
5921 level);
5922 }
5923
5924 // always do barrier because of layout transitions
5925 VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
5926 renderer,
5927 commandBuffer,
5928 destinationUsageMode,
5929 textureSubresource);
5930
5931 return textureSubresource;
5932}
5933
5934static VkRenderPass VULKAN_INTERNAL_CreateRenderPass(
5935 VulkanRenderer *renderer,
5936 const SDL_GPUColorTargetInfo *colorTargetInfos,
5937 Uint32 numColorTargets,
5938 const SDL_GPUDepthStencilTargetInfo *depthStencilTargetInfo)
5939{
5940 VkResult vulkanResult;
5941 VkAttachmentDescription attachmentDescriptions[2 * MAX_COLOR_TARGET_BINDINGS + 1 /* depth */];
5942 VkAttachmentReference colorAttachmentReferences[MAX_COLOR_TARGET_BINDINGS];
5943 VkAttachmentReference resolveReferences[MAX_COLOR_TARGET_BINDINGS];
5944 VkAttachmentReference depthStencilAttachmentReference;
5945 VkRenderPassCreateInfo renderPassCreateInfo;
5946 VkSubpassDescription subpass;
5947 VkRenderPass renderPass;
5948 Uint32 i;
5949
5950 Uint32 attachmentDescriptionCount = 0;
5951 Uint32 colorAttachmentReferenceCount = 0;
5952 Uint32 resolveReferenceCount = 0;
5953
5954 for (i = 0; i < numColorTargets; i += 1) {
5955 VulkanTextureContainer *container = (VulkanTextureContainer *)colorTargetInfos[i].texture;
5956 attachmentDescriptions[attachmentDescriptionCount].flags = 0;
5957 attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_TextureFormat[container->header.info.format];
5958 attachmentDescriptions[attachmentDescriptionCount].samples = SDLToVK_SampleCount[container->header.info.sample_count];
5959 attachmentDescriptions[attachmentDescriptionCount].loadOp = SDLToVK_LoadOp[colorTargetInfos[i].load_op];
5960 attachmentDescriptions[attachmentDescriptionCount].storeOp = SDLToVK_StoreOp[colorTargetInfos[i].store_op];
5961 attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
5962 attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
5963 attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
5964 attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
5965
5966 colorAttachmentReferences[colorAttachmentReferenceCount].attachment = attachmentDescriptionCount;
5967 colorAttachmentReferences[colorAttachmentReferenceCount].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
5968
5969 attachmentDescriptionCount += 1;
5970 colorAttachmentReferenceCount += 1;
5971
5972 if (colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE || colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) {
5973 VulkanTextureContainer *resolveContainer = (VulkanTextureContainer *)colorTargetInfos[i].resolve_texture;
5974
5975 attachmentDescriptions[attachmentDescriptionCount].flags = 0;
5976 attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_TextureFormat[resolveContainer->header.info.format];
5977 attachmentDescriptions[attachmentDescriptionCount].samples = SDLToVK_SampleCount[resolveContainer->header.info.sample_count];
5978 attachmentDescriptions[attachmentDescriptionCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // The texture will be overwritten anyway
5979 attachmentDescriptions[attachmentDescriptionCount].storeOp = VK_ATTACHMENT_STORE_OP_STORE; // Always store the resolve texture
5980 attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
5981 attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
5982 attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
5983 attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
5984
5985 resolveReferences[resolveReferenceCount].attachment = attachmentDescriptionCount;
5986 resolveReferences[resolveReferenceCount].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
5987
5988 attachmentDescriptionCount += 1;
5989 resolveReferenceCount += 1;
5990 }
5991 }
5992
5993 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
5994 subpass.flags = 0;
5995 subpass.inputAttachmentCount = 0;
5996 subpass.pInputAttachments = NULL;
5997 subpass.colorAttachmentCount = numColorTargets;
5998 subpass.pColorAttachments = colorAttachmentReferences;
5999 subpass.preserveAttachmentCount = 0;
6000 subpass.pPreserveAttachments = NULL;
6001
6002 if (depthStencilTargetInfo == NULL) {
6003 subpass.pDepthStencilAttachment = NULL;
6004 } else {
6005 VulkanTextureContainer *container = (VulkanTextureContainer *)depthStencilTargetInfo->texture;
6006
6007 attachmentDescriptions[attachmentDescriptionCount].flags = 0;
6008 attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_TextureFormat[container->header.info.format];
6009 attachmentDescriptions[attachmentDescriptionCount].samples = SDLToVK_SampleCount[container->header.info.sample_count];
6010 attachmentDescriptions[attachmentDescriptionCount].loadOp = SDLToVK_LoadOp[depthStencilTargetInfo->load_op];
6011 attachmentDescriptions[attachmentDescriptionCount].storeOp = SDLToVK_StoreOp[depthStencilTargetInfo->store_op];
6012 attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = SDLToVK_LoadOp[depthStencilTargetInfo->stencil_load_op];
6013 attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = SDLToVK_StoreOp[depthStencilTargetInfo->stencil_store_op];
6014 attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
6015 attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
6016
6017 depthStencilAttachmentReference.attachment = attachmentDescriptionCount;
6018 depthStencilAttachmentReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
6019
6020 subpass.pDepthStencilAttachment = &depthStencilAttachmentReference;
6021
6022 attachmentDescriptionCount += 1;
6023 }
6024
6025 if (resolveReferenceCount > 0) {
6026 subpass.pResolveAttachments = resolveReferences;
6027 } else {
6028 subpass.pResolveAttachments = NULL;
6029 }
6030
6031 renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
6032 renderPassCreateInfo.pNext = NULL;
6033 renderPassCreateInfo.flags = 0;
6034 renderPassCreateInfo.pAttachments = attachmentDescriptions;
6035 renderPassCreateInfo.attachmentCount = attachmentDescriptionCount;
6036 renderPassCreateInfo.subpassCount = 1;
6037 renderPassCreateInfo.pSubpasses = &subpass;
6038 renderPassCreateInfo.dependencyCount = 0;
6039 renderPassCreateInfo.pDependencies = NULL;
6040
6041 vulkanResult = renderer->vkCreateRenderPass(
6042 renderer->logicalDevice,
6043 &renderPassCreateInfo,
6044 NULL,
6045 &renderPass);
6046
6047 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateRenderPass, VK_NULL_HANDLE);
6048
6049 return renderPass;
6050}
6051
6052static VkRenderPass VULKAN_INTERNAL_CreateTransientRenderPass(
6053 VulkanRenderer *renderer,
6054 SDL_GPUGraphicsPipelineTargetInfo targetInfo,
6055 VkSampleCountFlagBits sampleCount)
6056{
6057 VkAttachmentDescription attachmentDescriptions[MAX_COLOR_TARGET_BINDINGS + 1 /* depth */];
6058 VkAttachmentReference colorAttachmentReferences[MAX_COLOR_TARGET_BINDINGS];
6059 VkAttachmentReference depthStencilAttachmentReference;
6060 SDL_GPUColorTargetDescription attachmentDescription;
6061 VkSubpassDescription subpass;
6062 VkRenderPassCreateInfo renderPassCreateInfo;
6063 VkRenderPass renderPass;
6064 VkResult result;
6065
6066 Uint32 attachmentDescriptionCount = 0;
6067 Uint32 colorAttachmentReferenceCount = 0;
6068 Uint32 i;
6069
6070 for (i = 0; i < targetInfo.num_color_targets; i += 1) {
6071 attachmentDescription = targetInfo.color_target_descriptions[i];
6072
6073 attachmentDescriptions[attachmentDescriptionCount].flags = 0;
6074 attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_TextureFormat[attachmentDescription.format];
6075 attachmentDescriptions[attachmentDescriptionCount].samples = sampleCount;
6076 attachmentDescriptions[attachmentDescriptionCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
6077 attachmentDescriptions[attachmentDescriptionCount].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
6078 attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
6079 attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
6080 attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
6081 attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
6082
6083 colorAttachmentReferences[colorAttachmentReferenceCount].attachment = attachmentDescriptionCount;
6084 colorAttachmentReferences[colorAttachmentReferenceCount].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
6085
6086 attachmentDescriptionCount += 1;
6087 colorAttachmentReferenceCount += 1;
6088 }
6089
6090 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
6091 subpass.flags = 0;
6092 subpass.inputAttachmentCount = 0;
6093 subpass.pInputAttachments = NULL;
6094 subpass.colorAttachmentCount = targetInfo.num_color_targets;
6095 subpass.pColorAttachments = colorAttachmentReferences;
6096 subpass.preserveAttachmentCount = 0;
6097 subpass.pPreserveAttachments = NULL;
6098
6099 if (targetInfo.has_depth_stencil_target) {
6100 attachmentDescriptions[attachmentDescriptionCount].flags = 0;
6101 attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_TextureFormat[targetInfo.depth_stencil_format];
6102 attachmentDescriptions[attachmentDescriptionCount].samples = sampleCount;
6103 attachmentDescriptions[attachmentDescriptionCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
6104 attachmentDescriptions[attachmentDescriptionCount].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
6105 attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
6106 attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
6107 attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
6108 attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
6109
6110 depthStencilAttachmentReference.attachment = attachmentDescriptionCount;
6111 depthStencilAttachmentReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
6112
6113 subpass.pDepthStencilAttachment = &depthStencilAttachmentReference;
6114
6115 attachmentDescriptionCount += 1;
6116 } else {
6117 subpass.pDepthStencilAttachment = NULL;
6118 }
6119
6120 // Resolve attachments aren't needed for transient passes
6121 subpass.pResolveAttachments = NULL;
6122
6123 renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
6124 renderPassCreateInfo.pNext = NULL;
6125 renderPassCreateInfo.flags = 0;
6126 renderPassCreateInfo.pAttachments = attachmentDescriptions;
6127 renderPassCreateInfo.attachmentCount = attachmentDescriptionCount;
6128 renderPassCreateInfo.subpassCount = 1;
6129 renderPassCreateInfo.pSubpasses = &subpass;
6130 renderPassCreateInfo.dependencyCount = 0;
6131 renderPassCreateInfo.pDependencies = NULL;
6132
6133 result = renderer->vkCreateRenderPass(
6134 renderer->logicalDevice,
6135 &renderPassCreateInfo,
6136 NULL,
6137 &renderPass);
6138
6139 CHECK_VULKAN_ERROR_AND_RETURN(result, vkCreateRenderPass, VK_NULL_HANDLE);
6140
6141 return renderPass;
6142}
6143
6144static SDL_GPUGraphicsPipeline *VULKAN_CreateGraphicsPipeline(
6145 SDL_GPURenderer *driverData,
6146 const SDL_GPUGraphicsPipelineCreateInfo *createinfo)
6147{
6148 VkResult vulkanResult;
6149 Uint32 i;
6150
6151 VulkanGraphicsPipeline *graphicsPipeline = (VulkanGraphicsPipeline *)SDL_malloc(sizeof(VulkanGraphicsPipeline));
6152 VkGraphicsPipelineCreateInfo vkPipelineCreateInfo;
6153
6154 VkPipelineShaderStageCreateInfo shaderStageCreateInfos[2];
6155
6156 VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo;
6157 VkVertexInputBindingDescription *vertexInputBindingDescriptions = SDL_stack_alloc(VkVertexInputBindingDescription, createinfo->vertex_input_state.num_vertex_buffers);
6158 VkVertexInputAttributeDescription *vertexInputAttributeDescriptions = SDL_stack_alloc(VkVertexInputAttributeDescription, createinfo->vertex_input_state.num_vertex_attributes);
6159
6160 VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo;
6161
6162 VkPipelineViewportStateCreateInfo viewportStateCreateInfo;
6163
6164 VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo;
6165
6166 VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo;
6167
6168 VkPipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo;
6169 VkStencilOpState frontStencilState;
6170 VkStencilOpState backStencilState;
6171
6172 VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo;
6173 VkPipelineColorBlendAttachmentState *colorBlendAttachmentStates = SDL_stack_alloc(
6174 VkPipelineColorBlendAttachmentState,
6175 createinfo->target_info.num_color_targets);
6176
6177 static const VkDynamicState dynamicStates[] = {
6178 VK_DYNAMIC_STATE_VIEWPORT,
6179 VK_DYNAMIC_STATE_SCISSOR,
6180 VK_DYNAMIC_STATE_BLEND_CONSTANTS,
6181 VK_DYNAMIC_STATE_STENCIL_REFERENCE
6182 };
6183 VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo;
6184
6185 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
6186
6187 // Create a "compatible" render pass
6188
6189 VkRenderPass transientRenderPass = VULKAN_INTERNAL_CreateTransientRenderPass(
6190 renderer,
6191 createinfo->target_info,
6192 SDLToVK_SampleCount[createinfo->multisample_state.sample_count]);
6193
6194 // Dynamic state
6195
6196 dynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
6197 dynamicStateCreateInfo.pNext = NULL;
6198 dynamicStateCreateInfo.flags = 0;
6199 dynamicStateCreateInfo.dynamicStateCount = SDL_arraysize(dynamicStates);
6200 dynamicStateCreateInfo.pDynamicStates = dynamicStates;
6201
6202 // Shader stages
6203
6204 graphicsPipeline->vertexShader = (VulkanShader *)createinfo->vertex_shader;
6205 SDL_AtomicIncRef(&graphicsPipeline->vertexShader->referenceCount);
6206
6207 shaderStageCreateInfos[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
6208 shaderStageCreateInfos[0].pNext = NULL;
6209 shaderStageCreateInfos[0].flags = 0;
6210 shaderStageCreateInfos[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
6211 shaderStageCreateInfos[0].module = graphicsPipeline->vertexShader->shaderModule;
6212 shaderStageCreateInfos[0].pName = graphicsPipeline->vertexShader->entrypointName;
6213 shaderStageCreateInfos[0].pSpecializationInfo = NULL;
6214
6215 graphicsPipeline->fragmentShader = (VulkanShader *)createinfo->fragment_shader;
6216 SDL_AtomicIncRef(&graphicsPipeline->fragmentShader->referenceCount);
6217
6218 shaderStageCreateInfos[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
6219 shaderStageCreateInfos[1].pNext = NULL;
6220 shaderStageCreateInfos[1].flags = 0;
6221 shaderStageCreateInfos[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
6222 shaderStageCreateInfos[1].module = graphicsPipeline->fragmentShader->shaderModule;
6223 shaderStageCreateInfos[1].pName = graphicsPipeline->fragmentShader->entrypointName;
6224 shaderStageCreateInfos[1].pSpecializationInfo = NULL;
6225
6226 if (renderer->debugMode) {
6227 if (graphicsPipeline->vertexShader->stage != SDL_GPU_SHADERSTAGE_VERTEX) {
6228 SDL_assert_release(!"CreateGraphicsPipeline was passed a fragment shader for the vertex stage");
6229 }
6230 if (graphicsPipeline->fragmentShader->stage != SDL_GPU_SHADERSTAGE_FRAGMENT) {
6231 SDL_assert_release(!"CreateGraphicsPipeline was passed a vertex shader for the fragment stage");
6232 }
6233 }
6234
6235 // Vertex input
6236
6237 for (i = 0; i < createinfo->vertex_input_state.num_vertex_buffers; i += 1) {
6238 vertexInputBindingDescriptions[i].binding = createinfo->vertex_input_state.vertex_buffer_descriptions[i].slot;
6239 vertexInputBindingDescriptions[i].inputRate = SDLToVK_VertexInputRate[createinfo->vertex_input_state.vertex_buffer_descriptions[i].input_rate];
6240 vertexInputBindingDescriptions[i].stride = createinfo->vertex_input_state.vertex_buffer_descriptions[i].pitch;
6241 }
6242
6243 for (i = 0; i < createinfo->vertex_input_state.num_vertex_attributes; i += 1) {
6244 vertexInputAttributeDescriptions[i].binding = createinfo->vertex_input_state.vertex_attributes[i].buffer_slot;
6245 vertexInputAttributeDescriptions[i].format = SDLToVK_VertexFormat[createinfo->vertex_input_state.vertex_attributes[i].format];
6246 vertexInputAttributeDescriptions[i].location = createinfo->vertex_input_state.vertex_attributes[i].location;
6247 vertexInputAttributeDescriptions[i].offset = createinfo->vertex_input_state.vertex_attributes[i].offset;
6248 }
6249
6250 vertexInputStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
6251 vertexInputStateCreateInfo.pNext = NULL;
6252 vertexInputStateCreateInfo.flags = 0;
6253 vertexInputStateCreateInfo.vertexBindingDescriptionCount = createinfo->vertex_input_state.num_vertex_buffers;
6254 vertexInputStateCreateInfo.pVertexBindingDescriptions = vertexInputBindingDescriptions;
6255 vertexInputStateCreateInfo.vertexAttributeDescriptionCount = createinfo->vertex_input_state.num_vertex_attributes;
6256 vertexInputStateCreateInfo.pVertexAttributeDescriptions = vertexInputAttributeDescriptions;
6257
6258 // Topology
6259
6260 inputAssemblyStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
6261 inputAssemblyStateCreateInfo.pNext = NULL;
6262 inputAssemblyStateCreateInfo.flags = 0;
6263 inputAssemblyStateCreateInfo.primitiveRestartEnable = VK_FALSE;
6264 inputAssemblyStateCreateInfo.topology = SDLToVK_PrimitiveType[createinfo->primitive_type];
6265
6266 graphicsPipeline->primitiveType = createinfo->primitive_type;
6267
6268 // Viewport
6269
6270 // NOTE: viewport and scissor are dynamic, and must be set using the command buffer
6271
6272 viewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
6273 viewportStateCreateInfo.pNext = NULL;
6274 viewportStateCreateInfo.flags = 0;
6275 viewportStateCreateInfo.viewportCount = 1;
6276 viewportStateCreateInfo.pViewports = NULL;
6277 viewportStateCreateInfo.scissorCount = 1;
6278 viewportStateCreateInfo.pScissors = NULL;
6279
6280 // Rasterization
6281
6282 rasterizationStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
6283 rasterizationStateCreateInfo.pNext = NULL;
6284 rasterizationStateCreateInfo.flags = 0;
6285 rasterizationStateCreateInfo.depthClampEnable = !createinfo->rasterizer_state.enable_depth_clip;
6286 rasterizationStateCreateInfo.rasterizerDiscardEnable = VK_FALSE;
6287 rasterizationStateCreateInfo.polygonMode = SDLToVK_PolygonMode(
6288 renderer,
6289 createinfo->rasterizer_state.fill_mode);
6290 rasterizationStateCreateInfo.cullMode = SDLToVK_CullMode[createinfo->rasterizer_state.cull_mode];
6291 rasterizationStateCreateInfo.frontFace = SDLToVK_FrontFace[createinfo->rasterizer_state.front_face];
6292 rasterizationStateCreateInfo.depthBiasEnable =
6293 createinfo->rasterizer_state.enable_depth_bias;
6294 rasterizationStateCreateInfo.depthBiasConstantFactor =
6295 createinfo->rasterizer_state.depth_bias_constant_factor;
6296 rasterizationStateCreateInfo.depthBiasClamp =
6297 createinfo->rasterizer_state.depth_bias_clamp;
6298 rasterizationStateCreateInfo.depthBiasSlopeFactor =
6299 createinfo->rasterizer_state.depth_bias_slope_factor;
6300 rasterizationStateCreateInfo.lineWidth = 1.0f;
6301
6302 // Multisample
6303
6304 Uint32 sampleMask = 0xFFFFFFFF;
6305
6306 multisampleStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
6307 multisampleStateCreateInfo.pNext = NULL;
6308 multisampleStateCreateInfo.flags = 0;
6309 multisampleStateCreateInfo.rasterizationSamples = SDLToVK_SampleCount[createinfo->multisample_state.sample_count];
6310 multisampleStateCreateInfo.sampleShadingEnable = VK_FALSE;
6311 multisampleStateCreateInfo.minSampleShading = 1.0f;
6312 multisampleStateCreateInfo.pSampleMask = &sampleMask;
6313 multisampleStateCreateInfo.alphaToCoverageEnable = VK_FALSE;
6314 multisampleStateCreateInfo.alphaToOneEnable = VK_FALSE;
6315
6316 // Depth Stencil State
6317
6318 frontStencilState.failOp = SDLToVK_StencilOp[createinfo->depth_stencil_state.front_stencil_state.fail_op];
6319 frontStencilState.passOp = SDLToVK_StencilOp[createinfo->depth_stencil_state.front_stencil_state.pass_op];
6320 frontStencilState.depthFailOp = SDLToVK_StencilOp[createinfo->depth_stencil_state.front_stencil_state.depth_fail_op];
6321 frontStencilState.compareOp = SDLToVK_CompareOp[createinfo->depth_stencil_state.front_stencil_state.compare_op];
6322 frontStencilState.compareMask =
6323 createinfo->depth_stencil_state.compare_mask;
6324 frontStencilState.writeMask =
6325 createinfo->depth_stencil_state.write_mask;
6326 frontStencilState.reference = 0;
6327
6328 backStencilState.failOp = SDLToVK_StencilOp[createinfo->depth_stencil_state.back_stencil_state.fail_op];
6329 backStencilState.passOp = SDLToVK_StencilOp[createinfo->depth_stencil_state.back_stencil_state.pass_op];
6330 backStencilState.depthFailOp = SDLToVK_StencilOp[createinfo->depth_stencil_state.back_stencil_state.depth_fail_op];
6331 backStencilState.compareOp = SDLToVK_CompareOp[createinfo->depth_stencil_state.back_stencil_state.compare_op];
6332 backStencilState.compareMask =
6333 createinfo->depth_stencil_state.compare_mask;
6334 backStencilState.writeMask =
6335 createinfo->depth_stencil_state.write_mask;
6336 backStencilState.reference = 0;
6337
6338 depthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
6339 depthStencilStateCreateInfo.pNext = NULL;
6340 depthStencilStateCreateInfo.flags = 0;
6341 depthStencilStateCreateInfo.depthTestEnable =
6342 createinfo->depth_stencil_state.enable_depth_test;
6343 depthStencilStateCreateInfo.depthWriteEnable =
6344 createinfo->depth_stencil_state.enable_depth_write;
6345 depthStencilStateCreateInfo.depthCompareOp = SDLToVK_CompareOp[createinfo->depth_stencil_state.compare_op];
6346 depthStencilStateCreateInfo.depthBoundsTestEnable = VK_FALSE;
6347 depthStencilStateCreateInfo.stencilTestEnable =
6348 createinfo->depth_stencil_state.enable_stencil_test;
6349 depthStencilStateCreateInfo.front = frontStencilState;
6350 depthStencilStateCreateInfo.back = backStencilState;
6351 depthStencilStateCreateInfo.minDepthBounds = 0; // unused
6352 depthStencilStateCreateInfo.maxDepthBounds = 0; // unused
6353
6354 // Color Blend
6355
6356 for (i = 0; i < createinfo->target_info.num_color_targets; i += 1) {
6357 SDL_GPUColorTargetBlendState blendState = createinfo->target_info.color_target_descriptions[i].blend_state;
6358 SDL_GPUColorComponentFlags colorWriteMask = blendState.enable_color_write_mask ?
6359 blendState.color_write_mask :
6360 0xF;
6361
6362 colorBlendAttachmentStates[i].blendEnable =
6363 blendState.enable_blend;
6364 colorBlendAttachmentStates[i].srcColorBlendFactor = SDLToVK_BlendFactor[blendState.src_color_blendfactor];
6365 colorBlendAttachmentStates[i].dstColorBlendFactor = SDLToVK_BlendFactor[blendState.dst_color_blendfactor];
6366 colorBlendAttachmentStates[i].colorBlendOp = SDLToVK_BlendOp[blendState.color_blend_op];
6367 colorBlendAttachmentStates[i].srcAlphaBlendFactor = SDLToVK_BlendFactor[blendState.src_alpha_blendfactor];
6368 colorBlendAttachmentStates[i].dstAlphaBlendFactor = SDLToVK_BlendFactor[blendState.dst_alpha_blendfactor];
6369 colorBlendAttachmentStates[i].alphaBlendOp = SDLToVK_BlendOp[blendState.alpha_blend_op];
6370 colorBlendAttachmentStates[i].colorWriteMask =
6371 colorWriteMask;
6372 }
6373
6374 colorBlendStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
6375 colorBlendStateCreateInfo.pNext = NULL;
6376 colorBlendStateCreateInfo.flags = 0;
6377 colorBlendStateCreateInfo.attachmentCount =
6378 createinfo->target_info.num_color_targets;
6379 colorBlendStateCreateInfo.pAttachments =
6380 colorBlendAttachmentStates;
6381 colorBlendStateCreateInfo.blendConstants[0] = 1.0f;
6382 colorBlendStateCreateInfo.blendConstants[1] = 1.0f;
6383 colorBlendStateCreateInfo.blendConstants[2] = 1.0f;
6384 colorBlendStateCreateInfo.blendConstants[3] = 1.0f;
6385
6386 // We don't support LogicOp, so this is easy.
6387 colorBlendStateCreateInfo.logicOpEnable = VK_FALSE;
6388 colorBlendStateCreateInfo.logicOp = 0;
6389
6390 // Pipeline Layout
6391
6392 graphicsPipeline->resourceLayout =
6393 VULKAN_INTERNAL_FetchGraphicsPipelineResourceLayout(
6394 renderer,
6395 graphicsPipeline->vertexShader,
6396 graphicsPipeline->fragmentShader);
6397
6398 if (graphicsPipeline->resourceLayout == NULL) {
6399 SDL_stack_free(vertexInputBindingDescriptions);
6400 SDL_stack_free(vertexInputAttributeDescriptions);
6401 SDL_stack_free(colorBlendAttachmentStates);
6402 SDL_free(graphicsPipeline);
6403 SET_STRING_ERROR_AND_RETURN("Failed to initialize pipeline resource layout!", NULL);
6404 }
6405
6406 // Pipeline
6407
6408 vkPipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
6409 vkPipelineCreateInfo.pNext = NULL;
6410 vkPipelineCreateInfo.flags = 0;
6411 vkPipelineCreateInfo.stageCount = 2;
6412 vkPipelineCreateInfo.pStages = shaderStageCreateInfos;
6413 vkPipelineCreateInfo.pVertexInputState = &vertexInputStateCreateInfo;
6414 vkPipelineCreateInfo.pInputAssemblyState = &inputAssemblyStateCreateInfo;
6415 vkPipelineCreateInfo.pTessellationState = VK_NULL_HANDLE;
6416 vkPipelineCreateInfo.pViewportState = &viewportStateCreateInfo;
6417 vkPipelineCreateInfo.pRasterizationState = &rasterizationStateCreateInfo;
6418 vkPipelineCreateInfo.pMultisampleState = &multisampleStateCreateInfo;
6419 vkPipelineCreateInfo.pDepthStencilState = &depthStencilStateCreateInfo;
6420 vkPipelineCreateInfo.pColorBlendState = &colorBlendStateCreateInfo;
6421 vkPipelineCreateInfo.pDynamicState = &dynamicStateCreateInfo;
6422 vkPipelineCreateInfo.layout = graphicsPipeline->resourceLayout->pipelineLayout;
6423 vkPipelineCreateInfo.renderPass = transientRenderPass;
6424 vkPipelineCreateInfo.subpass = 0;
6425 vkPipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE;
6426 vkPipelineCreateInfo.basePipelineIndex = 0;
6427
6428 // TODO: enable pipeline caching
6429 vulkanResult = renderer->vkCreateGraphicsPipelines(
6430 renderer->logicalDevice,
6431 VK_NULL_HANDLE,
6432 1,
6433 &vkPipelineCreateInfo,
6434 NULL,
6435 &graphicsPipeline->pipeline);
6436
6437 SDL_stack_free(vertexInputBindingDescriptions);
6438 SDL_stack_free(vertexInputAttributeDescriptions);
6439 SDL_stack_free(colorBlendAttachmentStates);
6440
6441 renderer->vkDestroyRenderPass(
6442 renderer->logicalDevice,
6443 transientRenderPass,
6444 NULL);
6445
6446 if (vulkanResult != VK_SUCCESS) {
6447 SDL_free(graphicsPipeline);
6448 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateGraphicsPipelines, NULL);
6449 }
6450
6451 SDL_SetAtomicInt(&graphicsPipeline->referenceCount, 0);
6452
6453 if (renderer->debugMode && renderer->supportsDebugUtils && SDL_HasProperty(createinfo->props, SDL_PROP_GPU_GRAPHICSPIPELINE_CREATE_NAME_STRING)) {
6454 VkDebugUtilsObjectNameInfoEXT nameInfo;
6455 nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
6456 nameInfo.pNext = NULL;
6457 nameInfo.pObjectName = SDL_GetStringProperty(createinfo->props, SDL_PROP_GPU_GRAPHICSPIPELINE_CREATE_NAME_STRING, NULL);
6458 nameInfo.objectType = VK_OBJECT_TYPE_PIPELINE;
6459 nameInfo.objectHandle = (uint64_t)graphicsPipeline->pipeline;
6460
6461 renderer->vkSetDebugUtilsObjectNameEXT(
6462 renderer->logicalDevice,
6463 &nameInfo);
6464 }
6465
6466 return (SDL_GPUGraphicsPipeline *)graphicsPipeline;
6467}
6468
6469static SDL_GPUComputePipeline *VULKAN_CreateComputePipeline(
6470 SDL_GPURenderer *driverData,
6471 const SDL_GPUComputePipelineCreateInfo *createinfo)
6472{
6473 VkShaderModuleCreateInfo shaderModuleCreateInfo;
6474 VkComputePipelineCreateInfo vkShaderCreateInfo;
6475 VkPipelineShaderStageCreateInfo pipelineShaderStageCreateInfo;
6476 VkResult vulkanResult;
6477 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
6478 VulkanComputePipeline *vulkanComputePipeline;
6479
6480 if (createinfo->format != SDL_GPU_SHADERFORMAT_SPIRV) {
6481 SET_STRING_ERROR_AND_RETURN("Incompatible shader format for Vulkan!", NULL);
6482 }
6483
6484 vulkanComputePipeline = SDL_malloc(sizeof(VulkanComputePipeline));
6485 shaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
6486 shaderModuleCreateInfo.pNext = NULL;
6487 shaderModuleCreateInfo.flags = 0;
6488 shaderModuleCreateInfo.codeSize = createinfo->code_size;
6489 shaderModuleCreateInfo.pCode = (Uint32 *)createinfo->code;
6490
6491 vulkanResult = renderer->vkCreateShaderModule(
6492 renderer->logicalDevice,
6493 &shaderModuleCreateInfo,
6494 NULL,
6495 &vulkanComputePipeline->shaderModule);
6496
6497 if (vulkanResult != VK_SUCCESS) {
6498 SDL_free(vulkanComputePipeline);
6499 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateShaderModule, NULL);
6500 }
6501
6502 pipelineShaderStageCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
6503 pipelineShaderStageCreateInfo.pNext = NULL;
6504 pipelineShaderStageCreateInfo.flags = 0;
6505 pipelineShaderStageCreateInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;
6506 pipelineShaderStageCreateInfo.module = vulkanComputePipeline->shaderModule;
6507 pipelineShaderStageCreateInfo.pName = createinfo->entrypoint;
6508 pipelineShaderStageCreateInfo.pSpecializationInfo = NULL;
6509
6510 vulkanComputePipeline->resourceLayout = VULKAN_INTERNAL_FetchComputePipelineResourceLayout(
6511 renderer,
6512 createinfo);
6513
6514 if (vulkanComputePipeline->resourceLayout == NULL) {
6515 renderer->vkDestroyShaderModule(
6516 renderer->logicalDevice,
6517 vulkanComputePipeline->shaderModule,
6518 NULL);
6519 SDL_free(vulkanComputePipeline);
6520 return NULL;
6521 }
6522
6523 vkShaderCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
6524 vkShaderCreateInfo.pNext = NULL;
6525 vkShaderCreateInfo.flags = 0;
6526 vkShaderCreateInfo.stage = pipelineShaderStageCreateInfo;
6527 vkShaderCreateInfo.layout = vulkanComputePipeline->resourceLayout->pipelineLayout;
6528 vkShaderCreateInfo.basePipelineHandle = (VkPipeline)VK_NULL_HANDLE;
6529 vkShaderCreateInfo.basePipelineIndex = 0;
6530
6531 vulkanResult = renderer->vkCreateComputePipelines(
6532 renderer->logicalDevice,
6533 (VkPipelineCache)VK_NULL_HANDLE,
6534 1,
6535 &vkShaderCreateInfo,
6536 NULL,
6537 &vulkanComputePipeline->pipeline);
6538
6539 if (vulkanResult != VK_SUCCESS) {
6540 VULKAN_INTERNAL_DestroyComputePipeline(renderer, vulkanComputePipeline);
6541 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateComputePipeline, NULL);
6542 return NULL;
6543 }
6544
6545 SDL_SetAtomicInt(&vulkanComputePipeline->referenceCount, 0);
6546
6547 if (renderer->debugMode && renderer->supportsDebugUtils && SDL_HasProperty(createinfo->props, SDL_PROP_GPU_COMPUTEPIPELINE_CREATE_NAME_STRING)) {
6548 VkDebugUtilsObjectNameInfoEXT nameInfo;
6549 nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
6550 nameInfo.pNext = NULL;
6551 nameInfo.pObjectName = SDL_GetStringProperty(createinfo->props, SDL_PROP_GPU_COMPUTEPIPELINE_CREATE_NAME_STRING, NULL);
6552 nameInfo.objectType = VK_OBJECT_TYPE_PIPELINE;
6553 nameInfo.objectHandle = (uint64_t)vulkanComputePipeline->pipeline;
6554
6555 renderer->vkSetDebugUtilsObjectNameEXT(
6556 renderer->logicalDevice,
6557 &nameInfo);
6558 }
6559
6560 return (SDL_GPUComputePipeline *)vulkanComputePipeline;
6561}
6562
6563static SDL_GPUSampler *VULKAN_CreateSampler(
6564 SDL_GPURenderer *driverData,
6565 const SDL_GPUSamplerCreateInfo *createinfo)
6566{
6567 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
6568 VulkanSampler *vulkanSampler = SDL_malloc(sizeof(VulkanSampler));
6569 VkResult vulkanResult;
6570
6571 VkSamplerCreateInfo vkSamplerCreateInfo;
6572 vkSamplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
6573 vkSamplerCreateInfo.pNext = NULL;
6574 vkSamplerCreateInfo.flags = 0;
6575 vkSamplerCreateInfo.magFilter = SDLToVK_Filter[createinfo->mag_filter];
6576 vkSamplerCreateInfo.minFilter = SDLToVK_Filter[createinfo->min_filter];
6577 vkSamplerCreateInfo.mipmapMode = SDLToVK_SamplerMipmapMode[createinfo->mipmap_mode];
6578 vkSamplerCreateInfo.addressModeU = SDLToVK_SamplerAddressMode[createinfo->address_mode_u];
6579 vkSamplerCreateInfo.addressModeV = SDLToVK_SamplerAddressMode[createinfo->address_mode_v];
6580 vkSamplerCreateInfo.addressModeW = SDLToVK_SamplerAddressMode[createinfo->address_mode_w];
6581 vkSamplerCreateInfo.mipLodBias = createinfo->mip_lod_bias;
6582 vkSamplerCreateInfo.anisotropyEnable = createinfo->enable_anisotropy;
6583 vkSamplerCreateInfo.maxAnisotropy = createinfo->max_anisotropy;
6584 vkSamplerCreateInfo.compareEnable = createinfo->enable_compare;
6585 vkSamplerCreateInfo.compareOp = SDLToVK_CompareOp[createinfo->compare_op];
6586 vkSamplerCreateInfo.minLod = createinfo->min_lod;
6587 vkSamplerCreateInfo.maxLod = createinfo->max_lod;
6588 vkSamplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; // arbitrary, unused
6589 vkSamplerCreateInfo.unnormalizedCoordinates = VK_FALSE;
6590
6591 vulkanResult = renderer->vkCreateSampler(
6592 renderer->logicalDevice,
6593 &vkSamplerCreateInfo,
6594 NULL,
6595 &vulkanSampler->sampler);
6596
6597 if (vulkanResult != VK_SUCCESS) {
6598 SDL_free(vulkanSampler);
6599 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateSampler, NULL);
6600 }
6601
6602 SDL_SetAtomicInt(&vulkanSampler->referenceCount, 0);
6603
6604 if (renderer->debugMode && renderer->supportsDebugUtils && SDL_HasProperty(createinfo->props, SDL_PROP_GPU_SAMPLER_CREATE_NAME_STRING)) {
6605 VkDebugUtilsObjectNameInfoEXT nameInfo;
6606 nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
6607 nameInfo.pNext = NULL;
6608 nameInfo.pObjectName = SDL_GetStringProperty(createinfo->props, SDL_PROP_GPU_SAMPLER_CREATE_NAME_STRING, NULL);
6609 nameInfo.objectType = VK_OBJECT_TYPE_SAMPLER;
6610 nameInfo.objectHandle = (uint64_t)vulkanSampler->sampler;
6611
6612 renderer->vkSetDebugUtilsObjectNameEXT(
6613 renderer->logicalDevice,
6614 &nameInfo);
6615 }
6616
6617 return (SDL_GPUSampler *)vulkanSampler;
6618}
6619
6620static SDL_GPUShader *VULKAN_CreateShader(
6621 SDL_GPURenderer *driverData,
6622 const SDL_GPUShaderCreateInfo *createinfo)
6623{
6624 VulkanShader *vulkanShader;
6625 VkResult vulkanResult;
6626 VkShaderModuleCreateInfo vkShaderModuleCreateInfo;
6627 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
6628
6629 vulkanShader = SDL_malloc(sizeof(VulkanShader));
6630 vkShaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
6631 vkShaderModuleCreateInfo.pNext = NULL;
6632 vkShaderModuleCreateInfo.flags = 0;
6633 vkShaderModuleCreateInfo.codeSize = createinfo->code_size;
6634 vkShaderModuleCreateInfo.pCode = (Uint32 *)createinfo->code;
6635
6636 vulkanResult = renderer->vkCreateShaderModule(
6637 renderer->logicalDevice,
6638 &vkShaderModuleCreateInfo,
6639 NULL,
6640 &vulkanShader->shaderModule);
6641
6642 if (vulkanResult != VK_SUCCESS) {
6643 SDL_free(vulkanShader);
6644 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateShaderModule, NULL);
6645 }
6646
6647 const char *entrypoint = createinfo->entrypoint;
6648 if (!entrypoint) {
6649 entrypoint = "main";
6650 }
6651 vulkanShader->entrypointName = SDL_strdup(entrypoint);
6652 vulkanShader->stage = createinfo->stage;
6653 vulkanShader->numSamplers = createinfo->num_samplers;
6654 vulkanShader->numStorageTextures = createinfo->num_storage_textures;
6655 vulkanShader->numStorageBuffers = createinfo->num_storage_buffers;
6656 vulkanShader->numUniformBuffers = createinfo->num_uniform_buffers;
6657
6658 SDL_SetAtomicInt(&vulkanShader->referenceCount, 0);
6659
6660 if (renderer->debugMode && SDL_HasProperty(createinfo->props, SDL_PROP_GPU_SHADER_CREATE_NAME_STRING)) {
6661 VkDebugUtilsObjectNameInfoEXT nameInfo;
6662 nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
6663 nameInfo.pNext = NULL;
6664 nameInfo.pObjectName = SDL_GetStringProperty(createinfo->props, SDL_PROP_GPU_SHADER_CREATE_NAME_STRING, NULL);
6665 nameInfo.objectType = VK_OBJECT_TYPE_SHADER_MODULE;
6666 nameInfo.objectHandle = (uint64_t)vulkanShader->shaderModule;
6667
6668 renderer->vkSetDebugUtilsObjectNameEXT(
6669 renderer->logicalDevice,
6670 &nameInfo);
6671 }
6672
6673 return (SDL_GPUShader *)vulkanShader;
6674}
6675
6676static bool VULKAN_SupportsSampleCount(
6677 SDL_GPURenderer *driverData,
6678 SDL_GPUTextureFormat format,
6679 SDL_GPUSampleCount sampleCount)
6680{
6681 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
6682 VkSampleCountFlags bits = IsDepthFormat(format) ? renderer->physicalDeviceProperties.properties.limits.framebufferDepthSampleCounts : renderer->physicalDeviceProperties.properties.limits.framebufferColorSampleCounts;
6683 VkSampleCountFlagBits vkSampleCount = SDLToVK_SampleCount[sampleCount];
6684 return !!(bits & vkSampleCount);
6685}
6686
6687static SDL_GPUTexture *VULKAN_CreateTexture(
6688 SDL_GPURenderer *driverData,
6689 const SDL_GPUTextureCreateInfo *createinfo)
6690{
6691 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
6692 VulkanTexture *texture;
6693 VulkanTextureContainer *container;
6694
6695 texture = VULKAN_INTERNAL_CreateTexture(
6696 renderer,
6697 createinfo);
6698
6699 if (texture == NULL) {
6700 return NULL;
6701 }
6702
6703 container = SDL_malloc(sizeof(VulkanTextureContainer));
6704
6705 // Copy properties so we don't lose information when the client destroys them
6706 container->header.info = *createinfo;
6707 container->header.info.props = SDL_CreateProperties();
6708 SDL_CopyProperties(createinfo->props, container->header.info.props);
6709
6710 container->canBeCycled = true;
6711 container->activeTexture = texture;
6712 container->textureCapacity = 1;
6713 container->textureCount = 1;
6714 container->textures = SDL_malloc(
6715 container->textureCapacity * sizeof(VulkanTexture *));
6716 container->textures[0] = container->activeTexture;
6717 container->debugName = NULL;
6718
6719 if (SDL_HasProperty(createinfo->props, SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING)) {
6720 container->debugName = SDL_strdup(SDL_GetStringProperty(createinfo->props, SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING, NULL));
6721 }
6722
6723 texture->container = container;
6724 texture->containerIndex = 0;
6725
6726 return (SDL_GPUTexture *)container;
6727}
6728
6729static SDL_GPUBuffer *VULKAN_CreateBuffer(
6730 SDL_GPURenderer *driverData,
6731 SDL_GPUBufferUsageFlags usageFlags,
6732 Uint32 size,
6733 const char *debugName)
6734{
6735 return (SDL_GPUBuffer *)VULKAN_INTERNAL_CreateBufferContainer(
6736 (VulkanRenderer *)driverData,
6737 (VkDeviceSize)size,
6738 usageFlags,
6739 VULKAN_BUFFER_TYPE_GPU,
6740 false,
6741 debugName);
6742}
6743
6744static VulkanUniformBuffer *VULKAN_INTERNAL_CreateUniformBuffer(
6745 VulkanRenderer *renderer,
6746 Uint32 size)
6747{
6748 VulkanUniformBuffer *uniformBuffer = SDL_calloc(1, sizeof(VulkanUniformBuffer));
6749
6750 uniformBuffer->buffer = VULKAN_INTERNAL_CreateBuffer(
6751 renderer,
6752 (VkDeviceSize)size,
6753 0,
6754 VULKAN_BUFFER_TYPE_UNIFORM,
6755 false,
6756 NULL);
6757
6758 uniformBuffer->drawOffset = 0;
6759 uniformBuffer->writeOffset = 0;
6760 uniformBuffer->buffer->uniformBufferForDefrag = uniformBuffer;
6761
6762 return uniformBuffer;
6763}
6764
6765static SDL_GPUTransferBuffer *VULKAN_CreateTransferBuffer(
6766 SDL_GPURenderer *driverData,
6767 SDL_GPUTransferBufferUsage usage,
6768 Uint32 size,
6769 const char *debugName)
6770{
6771 return (SDL_GPUTransferBuffer *)VULKAN_INTERNAL_CreateBufferContainer(
6772 (VulkanRenderer *)driverData,
6773 (VkDeviceSize)size,
6774 0,
6775 VULKAN_BUFFER_TYPE_TRANSFER,
6776 true, // Dedicated allocations preserve the data even if a defrag is triggered.
6777 debugName);
6778}
6779
6780static void VULKAN_INTERNAL_ReleaseTexture(
6781 VulkanRenderer *renderer,
6782 VulkanTexture *vulkanTexture)
6783{
6784 if (vulkanTexture->markedForDestroy) {
6785 return;
6786 }
6787
6788 SDL_LockMutex(renderer->disposeLock);
6789
6790 EXPAND_ARRAY_IF_NEEDED(
6791 renderer->texturesToDestroy,
6792 VulkanTexture *,
6793 renderer->texturesToDestroyCount + 1,
6794 renderer->texturesToDestroyCapacity,
6795 renderer->texturesToDestroyCapacity * 2);
6796
6797 renderer->texturesToDestroy[renderer->texturesToDestroyCount] = vulkanTexture;
6798 renderer->texturesToDestroyCount += 1;
6799
6800 vulkanTexture->markedForDestroy = true;
6801
6802 SDL_UnlockMutex(renderer->disposeLock);
6803}
6804
6805static void VULKAN_ReleaseTexture(
6806 SDL_GPURenderer *driverData,
6807 SDL_GPUTexture *texture)
6808{
6809 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
6810 VulkanTextureContainer *vulkanTextureContainer = (VulkanTextureContainer *)texture;
6811 Uint32 i;
6812
6813 SDL_LockMutex(renderer->disposeLock);
6814
6815 for (i = 0; i < vulkanTextureContainer->textureCount; i += 1) {
6816 VULKAN_INTERNAL_ReleaseTexture(renderer, vulkanTextureContainer->textures[i]);
6817 }
6818
6819 // Containers are just client handles, so we can destroy immediately
6820 if (vulkanTextureContainer->debugName != NULL) {
6821 SDL_free(vulkanTextureContainer->debugName);
6822 }
6823 SDL_free(vulkanTextureContainer->textures);
6824 SDL_free(vulkanTextureContainer);
6825
6826 SDL_UnlockMutex(renderer->disposeLock);
6827}
6828
6829static void VULKAN_ReleaseSampler(
6830 SDL_GPURenderer *driverData,
6831 SDL_GPUSampler *sampler)
6832{
6833 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
6834 VulkanSampler *vulkanSampler = (VulkanSampler *)sampler;
6835
6836 SDL_LockMutex(renderer->disposeLock);
6837
6838 EXPAND_ARRAY_IF_NEEDED(
6839 renderer->samplersToDestroy,
6840 VulkanSampler *,
6841 renderer->samplersToDestroyCount + 1,
6842 renderer->samplersToDestroyCapacity,
6843 renderer->samplersToDestroyCapacity * 2);
6844
6845 renderer->samplersToDestroy[renderer->samplersToDestroyCount] = vulkanSampler;
6846 renderer->samplersToDestroyCount += 1;
6847
6848 SDL_UnlockMutex(renderer->disposeLock);
6849}
6850
6851static void VULKAN_INTERNAL_ReleaseBuffer(
6852 VulkanRenderer *renderer,
6853 VulkanBuffer *vulkanBuffer)
6854{
6855 if (vulkanBuffer->markedForDestroy) {
6856 return;
6857 }
6858
6859 SDL_LockMutex(renderer->disposeLock);
6860
6861 EXPAND_ARRAY_IF_NEEDED(
6862 renderer->buffersToDestroy,
6863 VulkanBuffer *,
6864 renderer->buffersToDestroyCount + 1,
6865 renderer->buffersToDestroyCapacity,
6866 renderer->buffersToDestroyCapacity * 2);
6867
6868 renderer->buffersToDestroy[renderer->buffersToDestroyCount] = vulkanBuffer;
6869 renderer->buffersToDestroyCount += 1;
6870
6871 vulkanBuffer->markedForDestroy = 1;
6872 vulkanBuffer->container = NULL;
6873
6874 SDL_UnlockMutex(renderer->disposeLock);
6875}
6876
6877static void VULKAN_INTERNAL_ReleaseBufferContainer(
6878 VulkanRenderer *renderer,
6879 VulkanBufferContainer *bufferContainer)
6880{
6881 Uint32 i;
6882
6883 SDL_LockMutex(renderer->disposeLock);
6884
6885 for (i = 0; i < bufferContainer->bufferCount; i += 1) {
6886 VULKAN_INTERNAL_ReleaseBuffer(renderer, bufferContainer->buffers[i]);
6887 }
6888
6889 // Containers are just client handles, so we can free immediately
6890 if (bufferContainer->debugName != NULL) {
6891 SDL_free(bufferContainer->debugName);
6892 bufferContainer->debugName = NULL;
6893 }
6894 SDL_free(bufferContainer->buffers);
6895 SDL_free(bufferContainer);
6896
6897 SDL_UnlockMutex(renderer->disposeLock);
6898}
6899
6900static void VULKAN_ReleaseBuffer(
6901 SDL_GPURenderer *driverData,
6902 SDL_GPUBuffer *buffer)
6903{
6904 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
6905 VulkanBufferContainer *vulkanBufferContainer = (VulkanBufferContainer *)buffer;
6906
6907 VULKAN_INTERNAL_ReleaseBufferContainer(
6908 renderer,
6909 vulkanBufferContainer);
6910}
6911
6912static void VULKAN_ReleaseTransferBuffer(
6913 SDL_GPURenderer *driverData,
6914 SDL_GPUTransferBuffer *transferBuffer)
6915{
6916 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
6917 VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer *)transferBuffer;
6918
6919 VULKAN_INTERNAL_ReleaseBufferContainer(
6920 renderer,
6921 transferBufferContainer);
6922}
6923
6924static void VULKAN_ReleaseShader(
6925 SDL_GPURenderer *driverData,
6926 SDL_GPUShader *shader)
6927{
6928 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
6929 VulkanShader *vulkanShader = (VulkanShader *)shader;
6930
6931 SDL_LockMutex(renderer->disposeLock);
6932
6933 EXPAND_ARRAY_IF_NEEDED(
6934 renderer->shadersToDestroy,
6935 VulkanShader *,
6936 renderer->shadersToDestroyCount + 1,
6937 renderer->shadersToDestroyCapacity,
6938 renderer->shadersToDestroyCapacity * 2);
6939
6940 renderer->shadersToDestroy[renderer->shadersToDestroyCount] = vulkanShader;
6941 renderer->shadersToDestroyCount += 1;
6942
6943 SDL_UnlockMutex(renderer->disposeLock);
6944}
6945
6946static void VULKAN_ReleaseComputePipeline(
6947 SDL_GPURenderer *driverData,
6948 SDL_GPUComputePipeline *computePipeline)
6949{
6950 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
6951 VulkanComputePipeline *vulkanComputePipeline = (VulkanComputePipeline *)computePipeline;
6952
6953 SDL_LockMutex(renderer->disposeLock);
6954
6955 EXPAND_ARRAY_IF_NEEDED(
6956 renderer->computePipelinesToDestroy,
6957 VulkanComputePipeline *,
6958 renderer->computePipelinesToDestroyCount + 1,
6959 renderer->computePipelinesToDestroyCapacity,
6960 renderer->computePipelinesToDestroyCapacity * 2);
6961
6962 renderer->computePipelinesToDestroy[renderer->computePipelinesToDestroyCount] = vulkanComputePipeline;
6963 renderer->computePipelinesToDestroyCount += 1;
6964
6965 SDL_UnlockMutex(renderer->disposeLock);
6966}
6967
6968static void VULKAN_ReleaseGraphicsPipeline(
6969 SDL_GPURenderer *driverData,
6970 SDL_GPUGraphicsPipeline *graphicsPipeline)
6971{
6972 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
6973 VulkanGraphicsPipeline *vulkanGraphicsPipeline = (VulkanGraphicsPipeline *)graphicsPipeline;
6974
6975 SDL_LockMutex(renderer->disposeLock);
6976
6977 EXPAND_ARRAY_IF_NEEDED(
6978 renderer->graphicsPipelinesToDestroy,
6979 VulkanGraphicsPipeline *,
6980 renderer->graphicsPipelinesToDestroyCount + 1,
6981 renderer->graphicsPipelinesToDestroyCapacity,
6982 renderer->graphicsPipelinesToDestroyCapacity * 2);
6983
6984 renderer->graphicsPipelinesToDestroy[renderer->graphicsPipelinesToDestroyCount] = vulkanGraphicsPipeline;
6985 renderer->graphicsPipelinesToDestroyCount += 1;
6986
6987 SDL_UnlockMutex(renderer->disposeLock);
6988}
6989
6990// Command Buffer render state
6991
6992static VkRenderPass VULKAN_INTERNAL_FetchRenderPass(
6993 VulkanRenderer *renderer,
6994 const SDL_GPUColorTargetInfo *colorTargetInfos,
6995 Uint32 numColorTargets,
6996 const SDL_GPUDepthStencilTargetInfo *depthStencilTargetInfo)
6997{
6998 VulkanRenderPassHashTableValue *renderPassWrapper = NULL;
6999 VkRenderPass renderPassHandle;
7000 RenderPassHashTableKey key;
7001 Uint32 i;
7002
7003 SDL_zero(key);
7004
7005 for (i = 0; i < numColorTargets; i += 1) {
7006 key.colorTargetDescriptions[i].format = SDLToVK_TextureFormat[((VulkanTextureContainer *)colorTargetInfos[i].texture)->header.info.format];
7007 key.colorTargetDescriptions[i].loadOp = colorTargetInfos[i].load_op;
7008 key.colorTargetDescriptions[i].storeOp = colorTargetInfos[i].store_op;
7009
7010 if (colorTargetInfos[i].resolve_texture != NULL) {
7011 key.resolveTargetFormats[key.numResolveTargets] = SDLToVK_TextureFormat[((VulkanTextureContainer *)colorTargetInfos[i].resolve_texture)->header.info.format];
7012 key.numResolveTargets += 1;
7013 }
7014 }
7015
7016 key.sampleCount = VK_SAMPLE_COUNT_1_BIT;
7017 if (numColorTargets > 0) {
7018 key.sampleCount = SDLToVK_SampleCount[((VulkanTextureContainer *)colorTargetInfos[0].texture)->header.info.sample_count];
7019 }
7020
7021 key.numColorTargets = numColorTargets;
7022
7023 if (depthStencilTargetInfo == NULL) {
7024 key.depthStencilTargetDescription.format = 0;
7025 key.depthStencilTargetDescription.loadOp = SDL_GPU_LOADOP_DONT_CARE;
7026 key.depthStencilTargetDescription.storeOp = SDL_GPU_STOREOP_DONT_CARE;
7027 key.depthStencilTargetDescription.stencilLoadOp = SDL_GPU_LOADOP_DONT_CARE;
7028 key.depthStencilTargetDescription.stencilStoreOp = SDL_GPU_STOREOP_DONT_CARE;
7029 } else {
7030 key.depthStencilTargetDescription.format = SDLToVK_TextureFormat[((VulkanTextureContainer *)depthStencilTargetInfo->texture)->header.info.format];
7031 key.depthStencilTargetDescription.loadOp = depthStencilTargetInfo->load_op;
7032 key.depthStencilTargetDescription.storeOp = depthStencilTargetInfo->store_op;
7033 key.depthStencilTargetDescription.stencilLoadOp = depthStencilTargetInfo->stencil_load_op;
7034 key.depthStencilTargetDescription.stencilStoreOp = depthStencilTargetInfo->stencil_store_op;
7035 }
7036
7037 bool result = SDL_FindInHashTable(
7038 renderer->renderPassHashTable,
7039 (const void *)&key,
7040 (const void **)&renderPassWrapper);
7041
7042 if (result) {
7043 return renderPassWrapper->handle;
7044 }
7045
7046 renderPassHandle = VULKAN_INTERNAL_CreateRenderPass(
7047 renderer,
7048 colorTargetInfos,
7049 numColorTargets,
7050 depthStencilTargetInfo);
7051
7052 if (renderPassHandle == VK_NULL_HANDLE) {
7053 return VK_NULL_HANDLE;
7054 }
7055
7056 // Have to malloc the key to store it in the hashtable
7057 RenderPassHashTableKey *allocedKey = SDL_malloc(sizeof(RenderPassHashTableKey));
7058 SDL_memcpy(allocedKey, &key, sizeof(RenderPassHashTableKey));
7059
7060 renderPassWrapper = SDL_malloc(sizeof(VulkanRenderPassHashTableValue));
7061 renderPassWrapper->handle = renderPassHandle;
7062
7063 SDL_InsertIntoHashTable(
7064 renderer->renderPassHashTable,
7065 (const void *)allocedKey,
7066 (const void *)renderPassWrapper, true);
7067
7068 return renderPassHandle;
7069}
7070
7071static VulkanFramebuffer *VULKAN_INTERNAL_FetchFramebuffer(
7072 VulkanRenderer *renderer,
7073 VkRenderPass renderPass,
7074 const SDL_GPUColorTargetInfo *colorTargetInfos,
7075 Uint32 numColorTargets,
7076 const SDL_GPUDepthStencilTargetInfo *depthStencilTargetInfo,
7077 Uint32 width,
7078 Uint32 height)
7079{
7080 VulkanFramebuffer *vulkanFramebuffer = NULL;
7081 VkFramebufferCreateInfo framebufferInfo;
7082 VkResult result;
7083 VkImageView imageViewAttachments[2 * MAX_COLOR_TARGET_BINDINGS + 1 /* depth */];
7084 FramebufferHashTableKey key;
7085 Uint32 attachmentCount = 0;
7086 Uint32 i;
7087
7088 SDL_zero(imageViewAttachments);
7089 SDL_zero(key);
7090
7091 key.numColorTargets = numColorTargets;
7092
7093 for (i = 0; i < numColorTargets; i += 1) {
7094 VulkanTextureContainer *container = (VulkanTextureContainer *)colorTargetInfos[i].texture;
7095 VulkanTextureSubresource *subresource = VULKAN_INTERNAL_FetchTextureSubresource(
7096 container,
7097 container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : colorTargetInfos[i].layer_or_depth_plane,
7098 colorTargetInfos[i].mip_level);
7099
7100 Uint32 rtvIndex =
7101 container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? colorTargetInfos[i].layer_or_depth_plane : 0;
7102 key.colorAttachmentViews[i] = subresource->renderTargetViews[rtvIndex];
7103
7104 if (colorTargetInfos[i].resolve_texture != NULL) {
7105 VulkanTextureContainer *resolveTextureContainer = (VulkanTextureContainer *)colorTargetInfos[i].resolve_texture;
7106 VulkanTextureSubresource *resolveSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
7107 resolveTextureContainer,
7108 colorTargetInfos[i].layer_or_depth_plane,
7109 colorTargetInfos[i].mip_level);
7110
7111 key.resolveAttachmentViews[key.numResolveAttachments] = resolveSubresource->renderTargetViews[0];
7112 key.numResolveAttachments += 1;
7113 }
7114 }
7115
7116 if (depthStencilTargetInfo == NULL) {
7117 key.depthStencilAttachmentView = VK_NULL_HANDLE;
7118 } else {
7119 VulkanTextureSubresource *subresource = VULKAN_INTERNAL_FetchTextureSubresource(
7120 (VulkanTextureContainer *)depthStencilTargetInfo->texture,
7121 0,
7122 0);
7123 key.depthStencilAttachmentView = subresource->depthStencilView;
7124 }
7125
7126 key.width = width;
7127 key.height = height;
7128
7129 SDL_LockMutex(renderer->framebufferFetchLock);
7130
7131 bool findResult = SDL_FindInHashTable(
7132 renderer->framebufferHashTable,
7133 (const void *)&key,
7134 (const void **)&vulkanFramebuffer);
7135
7136 SDL_UnlockMutex(renderer->framebufferFetchLock);
7137
7138 if (findResult) {
7139 return vulkanFramebuffer;
7140 }
7141
7142 vulkanFramebuffer = SDL_malloc(sizeof(VulkanFramebuffer));
7143
7144 SDL_SetAtomicInt(&vulkanFramebuffer->referenceCount, 0);
7145
7146 // Create a new framebuffer
7147
7148 for (i = 0; i < numColorTargets; i += 1) {
7149 VulkanTextureContainer *container = (VulkanTextureContainer *)colorTargetInfos[i].texture;
7150 VulkanTextureSubresource *subresource = VULKAN_INTERNAL_FetchTextureSubresource(
7151 container,
7152 container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : colorTargetInfos[i].layer_or_depth_plane,
7153 colorTargetInfos[i].mip_level);
7154
7155 Uint32 rtvIndex =
7156 container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? colorTargetInfos[i].layer_or_depth_plane : 0;
7157
7158 imageViewAttachments[attachmentCount] = subresource->renderTargetViews[rtvIndex];
7159
7160 attachmentCount += 1;
7161
7162 if (colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE || colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) {
7163 VulkanTextureContainer *resolveContainer = (VulkanTextureContainer *)colorTargetInfos[i].resolve_texture;
7164 VulkanTextureSubresource *resolveSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
7165 resolveContainer,
7166 colorTargetInfos[i].resolve_layer,
7167 colorTargetInfos[i].resolve_mip_level);
7168
7169 imageViewAttachments[attachmentCount] = resolveSubresource->renderTargetViews[0];
7170
7171 attachmentCount += 1;
7172 }
7173 }
7174
7175 if (depthStencilTargetInfo != NULL) {
7176 VulkanTextureSubresource *subresource = VULKAN_INTERNAL_FetchTextureSubresource(
7177 (VulkanTextureContainer *)depthStencilTargetInfo->texture,
7178 0,
7179 0);
7180 imageViewAttachments[attachmentCount] = subresource->depthStencilView;
7181
7182 attachmentCount += 1;
7183 }
7184
7185 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
7186 framebufferInfo.pNext = NULL;
7187 framebufferInfo.flags = 0;
7188 framebufferInfo.renderPass = renderPass;
7189 framebufferInfo.attachmentCount = attachmentCount;
7190 framebufferInfo.pAttachments = imageViewAttachments;
7191 framebufferInfo.width = key.width;
7192 framebufferInfo.height = key.height;
7193 framebufferInfo.layers = 1;
7194
7195 result = renderer->vkCreateFramebuffer(
7196 renderer->logicalDevice,
7197 &framebufferInfo,
7198 NULL,
7199 &vulkanFramebuffer->framebuffer);
7200
7201 if (result == VK_SUCCESS) {
7202 // Have to malloc the key to store it in the hashtable
7203 FramebufferHashTableKey *allocedKey = SDL_malloc(sizeof(FramebufferHashTableKey));
7204 SDL_memcpy(allocedKey, &key, sizeof(FramebufferHashTableKey));
7205
7206 SDL_LockMutex(renderer->framebufferFetchLock);
7207
7208 SDL_InsertIntoHashTable(
7209 renderer->framebufferHashTable,
7210 (const void *)allocedKey,
7211 (const void *)vulkanFramebuffer, true);
7212
7213 SDL_UnlockMutex(renderer->framebufferFetchLock);
7214 } else {
7215 SDL_free(vulkanFramebuffer);
7216 CHECK_VULKAN_ERROR_AND_RETURN(result, vkCreateFramebuffer, NULL);
7217 }
7218
7219 return vulkanFramebuffer;
7220}
7221
7222static void VULKAN_INTERNAL_SetCurrentViewport(
7223 VulkanCommandBuffer *commandBuffer,
7224 const SDL_GPUViewport *viewport)
7225{
7226 VulkanCommandBuffer *vulkanCommandBuffer = commandBuffer;
7227 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
7228
7229 vulkanCommandBuffer->currentViewport.x = viewport->x;
7230 vulkanCommandBuffer->currentViewport.width = viewport->w;
7231 vulkanCommandBuffer->currentViewport.minDepth = viewport->min_depth;
7232 vulkanCommandBuffer->currentViewport.maxDepth = viewport->max_depth;
7233
7234 // Viewport flip for consistency with other backends
7235 vulkanCommandBuffer->currentViewport.y = viewport->y + viewport->h;
7236 vulkanCommandBuffer->currentViewport.height = -viewport->h;
7237
7238 renderer->vkCmdSetViewport(
7239 vulkanCommandBuffer->commandBuffer,
7240 0,
7241 1,
7242 &vulkanCommandBuffer->currentViewport);
7243}
7244
7245static void VULKAN_SetViewport(
7246 SDL_GPUCommandBuffer *commandBuffer,
7247 const SDL_GPUViewport *viewport)
7248{
7249 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
7250
7251 VULKAN_INTERNAL_SetCurrentViewport(
7252 vulkanCommandBuffer,
7253 viewport);
7254}
7255
7256static void VULKAN_INTERNAL_SetCurrentScissor(
7257 VulkanCommandBuffer *vulkanCommandBuffer,
7258 const SDL_Rect *scissor)
7259{
7260 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
7261
7262 vulkanCommandBuffer->currentScissor.offset.x = scissor->x;
7263 vulkanCommandBuffer->currentScissor.offset.y = scissor->y;
7264 vulkanCommandBuffer->currentScissor.extent.width = scissor->w;
7265 vulkanCommandBuffer->currentScissor.extent.height = scissor->h;
7266
7267 renderer->vkCmdSetScissor(
7268 vulkanCommandBuffer->commandBuffer,
7269 0,
7270 1,
7271 &vulkanCommandBuffer->currentScissor);
7272}
7273
7274static void VULKAN_SetScissor(
7275 SDL_GPUCommandBuffer *commandBuffer,
7276 const SDL_Rect *scissor)
7277{
7278 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
7279
7280 VULKAN_INTERNAL_SetCurrentScissor(
7281 vulkanCommandBuffer,
7282 scissor);
7283}
7284
7285static void VULKAN_INTERNAL_SetCurrentBlendConstants(
7286 VulkanCommandBuffer *vulkanCommandBuffer,
7287 SDL_FColor blendConstants)
7288{
7289 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
7290
7291 vulkanCommandBuffer->blendConstants[0] = blendConstants.r;
7292 vulkanCommandBuffer->blendConstants[1] = blendConstants.g;
7293 vulkanCommandBuffer->blendConstants[2] = blendConstants.b;
7294 vulkanCommandBuffer->blendConstants[3] = blendConstants.a;
7295
7296 renderer->vkCmdSetBlendConstants(
7297 vulkanCommandBuffer->commandBuffer,
7298 vulkanCommandBuffer->blendConstants);
7299}
7300
7301static void VULKAN_SetBlendConstants(
7302 SDL_GPUCommandBuffer *commandBuffer,
7303 SDL_FColor blendConstants)
7304{
7305 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
7306
7307 VULKAN_INTERNAL_SetCurrentBlendConstants(
7308 vulkanCommandBuffer,
7309 blendConstants);
7310}
7311
7312static void VULKAN_INTERNAL_SetCurrentStencilReference(
7313 VulkanCommandBuffer *vulkanCommandBuffer,
7314 Uint8 reference)
7315{
7316 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
7317
7318 vulkanCommandBuffer->stencilRef = reference;
7319
7320 renderer->vkCmdSetStencilReference(
7321 vulkanCommandBuffer->commandBuffer,
7322 VK_STENCIL_FACE_FRONT_AND_BACK,
7323 vulkanCommandBuffer->stencilRef);
7324}
7325
7326static void VULKAN_SetStencilReference(
7327 SDL_GPUCommandBuffer *commandBuffer,
7328 Uint8 reference)
7329{
7330 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
7331
7332 VULKAN_INTERNAL_SetCurrentStencilReference(
7333 vulkanCommandBuffer,
7334 reference);
7335}
7336
7337static void VULKAN_BindVertexSamplers(
7338 SDL_GPUCommandBuffer *commandBuffer,
7339 Uint32 firstSlot,
7340 const SDL_GPUTextureSamplerBinding *textureSamplerBindings,
7341 Uint32 numBindings)
7342{
7343 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
7344
7345 for (Uint32 i = 0; i < numBindings; i += 1) {
7346 VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)textureSamplerBindings[i].texture;
7347 VulkanSampler *sampler = (VulkanSampler *)textureSamplerBindings[i].sampler;
7348
7349 if (vulkanCommandBuffer->vertexSamplers[firstSlot + i] != sampler) {
7350 VULKAN_INTERNAL_TrackSampler(
7351 vulkanCommandBuffer,
7352 (VulkanSampler *)textureSamplerBindings[i].sampler);
7353
7354 vulkanCommandBuffer->vertexSamplers[firstSlot + i] = (VulkanSampler *)textureSamplerBindings[i].sampler;
7355 vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
7356 }
7357
7358 if (vulkanCommandBuffer->vertexSamplerTextures[firstSlot + i] != textureContainer->activeTexture) {
7359 VULKAN_INTERNAL_TrackTexture(
7360 vulkanCommandBuffer,
7361 textureContainer->activeTexture);
7362
7363 vulkanCommandBuffer->vertexSamplerTextures[firstSlot + i] = textureContainer->activeTexture;
7364 vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
7365 }
7366 }
7367}
7368
7369static void VULKAN_BindVertexStorageTextures(
7370 SDL_GPUCommandBuffer *commandBuffer,
7371 Uint32 firstSlot,
7372 SDL_GPUTexture *const *storageTextures,
7373 Uint32 numBindings)
7374{
7375 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
7376
7377 for (Uint32 i = 0; i < numBindings; i += 1) {
7378 VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)storageTextures[i];
7379
7380 if (vulkanCommandBuffer->vertexStorageTextures[firstSlot + i] != textureContainer->activeTexture) {
7381 VULKAN_INTERNAL_TrackTexture(
7382 vulkanCommandBuffer,
7383 textureContainer->activeTexture);
7384
7385 vulkanCommandBuffer->vertexStorageTextures[firstSlot + i] = textureContainer->activeTexture;
7386 vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
7387 }
7388 }
7389}
7390
7391static void VULKAN_BindVertexStorageBuffers(
7392 SDL_GPUCommandBuffer *commandBuffer,
7393 Uint32 firstSlot,
7394 SDL_GPUBuffer *const *storageBuffers,
7395 Uint32 numBindings)
7396{
7397 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
7398
7399 for (Uint32 i = 0; i < numBindings; i += 1) {
7400 VulkanBufferContainer *bufferContainer = (VulkanBufferContainer *)storageBuffers[i];
7401
7402 if (vulkanCommandBuffer->vertexStorageBuffers[firstSlot + i] != bufferContainer->activeBuffer) {
7403 VULKAN_INTERNAL_TrackBuffer(
7404 vulkanCommandBuffer,
7405 bufferContainer->activeBuffer);
7406
7407 vulkanCommandBuffer->vertexStorageBuffers[firstSlot + i] = bufferContainer->activeBuffer;
7408 vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
7409 }
7410 }
7411}
7412
7413static void VULKAN_BindFragmentSamplers(
7414 SDL_GPUCommandBuffer *commandBuffer,
7415 Uint32 firstSlot,
7416 const SDL_GPUTextureSamplerBinding *textureSamplerBindings,
7417 Uint32 numBindings)
7418{
7419 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
7420
7421 for (Uint32 i = 0; i < numBindings; i += 1) {
7422 VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)textureSamplerBindings[i].texture;
7423 VulkanSampler *sampler = (VulkanSampler *)textureSamplerBindings[i].sampler;
7424
7425 if (vulkanCommandBuffer->fragmentSamplers[firstSlot + i] != sampler) {
7426 VULKAN_INTERNAL_TrackSampler(
7427 vulkanCommandBuffer,
7428 (VulkanSampler *)textureSamplerBindings[i].sampler);
7429
7430 vulkanCommandBuffer->fragmentSamplers[firstSlot + i] = (VulkanSampler *)textureSamplerBindings[i].sampler;
7431 vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
7432 }
7433
7434 if (vulkanCommandBuffer->fragmentSamplerTextures[firstSlot + i] != textureContainer->activeTexture) {
7435 VULKAN_INTERNAL_TrackTexture(
7436 vulkanCommandBuffer,
7437 textureContainer->activeTexture);
7438
7439 vulkanCommandBuffer->fragmentSamplerTextures[firstSlot + i] = textureContainer->activeTexture;
7440 vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
7441 }
7442 }
7443}
7444
7445static void VULKAN_BindFragmentStorageTextures(
7446 SDL_GPUCommandBuffer *commandBuffer,
7447 Uint32 firstSlot,
7448 SDL_GPUTexture *const *storageTextures,
7449 Uint32 numBindings)
7450{
7451 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
7452
7453 for (Uint32 i = 0; i < numBindings; i += 1) {
7454 VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)storageTextures[i];
7455
7456 if (vulkanCommandBuffer->fragmentStorageTextures[firstSlot + i] != textureContainer->activeTexture) {
7457 VULKAN_INTERNAL_TrackTexture(
7458 vulkanCommandBuffer,
7459 textureContainer->activeTexture);
7460
7461 vulkanCommandBuffer->fragmentStorageTextures[firstSlot + i] = textureContainer->activeTexture;
7462 vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
7463 }
7464 }
7465}
7466
7467static void VULKAN_BindFragmentStorageBuffers(
7468 SDL_GPUCommandBuffer *commandBuffer,
7469 Uint32 firstSlot,
7470 SDL_GPUBuffer *const *storageBuffers,
7471 Uint32 numBindings)
7472{
7473 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
7474 VulkanBufferContainer *bufferContainer;
7475 Uint32 i;
7476
7477 for (i = 0; i < numBindings; i += 1) {
7478 bufferContainer = (VulkanBufferContainer *)storageBuffers[i];
7479
7480 if (vulkanCommandBuffer->fragmentStorageBuffers[firstSlot + i] != bufferContainer->activeBuffer) {
7481 VULKAN_INTERNAL_TrackBuffer(
7482 vulkanCommandBuffer,
7483 bufferContainer->activeBuffer);
7484
7485 vulkanCommandBuffer->fragmentStorageBuffers[firstSlot + i] = bufferContainer->activeBuffer;
7486 vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
7487 }
7488 }
7489}
7490
7491static VulkanUniformBuffer *VULKAN_INTERNAL_AcquireUniformBufferFromPool(
7492 VulkanCommandBuffer *commandBuffer)
7493{
7494 VulkanRenderer *renderer = commandBuffer->renderer;
7495 VulkanUniformBuffer *uniformBuffer;
7496
7497 SDL_LockMutex(renderer->acquireUniformBufferLock);
7498
7499 if (renderer->uniformBufferPoolCount > 0) {
7500 uniformBuffer = renderer->uniformBufferPool[renderer->uniformBufferPoolCount - 1];
7501 renderer->uniformBufferPoolCount -= 1;
7502 } else {
7503 uniformBuffer = VULKAN_INTERNAL_CreateUniformBuffer(
7504 renderer,
7505 UNIFORM_BUFFER_SIZE);
7506 }
7507
7508 SDL_UnlockMutex(renderer->acquireUniformBufferLock);
7509
7510 VULKAN_INTERNAL_TrackUniformBuffer(commandBuffer, uniformBuffer);
7511
7512 return uniformBuffer;
7513}
7514
7515static void VULKAN_INTERNAL_ReturnUniformBufferToPool(
7516 VulkanRenderer *renderer,
7517 VulkanUniformBuffer *uniformBuffer)
7518{
7519 if (renderer->uniformBufferPoolCount >= renderer->uniformBufferPoolCapacity) {
7520 renderer->uniformBufferPoolCapacity *= 2;
7521 renderer->uniformBufferPool = SDL_realloc(
7522 renderer->uniformBufferPool,
7523 renderer->uniformBufferPoolCapacity * sizeof(VulkanUniformBuffer *));
7524 }
7525
7526 renderer->uniformBufferPool[renderer->uniformBufferPoolCount] = uniformBuffer;
7527 renderer->uniformBufferPoolCount += 1;
7528
7529 uniformBuffer->writeOffset = 0;
7530 uniformBuffer->drawOffset = 0;
7531}
7532
7533static void VULKAN_INTERNAL_PushUniformData(
7534 VulkanCommandBuffer *commandBuffer,
7535 VulkanUniformBufferStage uniformBufferStage,
7536 Uint32 slotIndex,
7537 const void *data,
7538 Uint32 length)
7539{
7540 Uint32 blockSize =
7541 VULKAN_INTERNAL_NextHighestAlignment32(
7542 length,
7543 commandBuffer->renderer->minUBOAlignment);
7544
7545 VulkanUniformBuffer *uniformBuffer;
7546
7547 if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_VERTEX) {
7548 if (commandBuffer->vertexUniformBuffers[slotIndex] == NULL) {
7549 commandBuffer->vertexUniformBuffers[slotIndex] = VULKAN_INTERNAL_AcquireUniformBufferFromPool(
7550 commandBuffer);
7551 }
7552 uniformBuffer = commandBuffer->vertexUniformBuffers[slotIndex];
7553 } else if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_FRAGMENT) {
7554 if (commandBuffer->fragmentUniformBuffers[slotIndex] == NULL) {
7555 commandBuffer->fragmentUniformBuffers[slotIndex] = VULKAN_INTERNAL_AcquireUniformBufferFromPool(
7556 commandBuffer);
7557 }
7558 uniformBuffer = commandBuffer->fragmentUniformBuffers[slotIndex];
7559 } else if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_COMPUTE) {
7560 if (commandBuffer->computeUniformBuffers[slotIndex] == NULL) {
7561 commandBuffer->computeUniformBuffers[slotIndex] = VULKAN_INTERNAL_AcquireUniformBufferFromPool(
7562 commandBuffer);
7563 }
7564 uniformBuffer = commandBuffer->computeUniformBuffers[slotIndex];
7565 } else {
7566 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!");
7567 return;
7568 }
7569
7570 // If there is no more room, acquire a new uniform buffer
7571 if (uniformBuffer->writeOffset + blockSize + MAX_UBO_SECTION_SIZE >= uniformBuffer->buffer->size) {
7572 uniformBuffer = VULKAN_INTERNAL_AcquireUniformBufferFromPool(commandBuffer);
7573
7574 uniformBuffer->drawOffset = 0;
7575 uniformBuffer->writeOffset = 0;
7576
7577 if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_VERTEX) {
7578 commandBuffer->vertexUniformBuffers[slotIndex] = uniformBuffer;
7579 commandBuffer->needNewVertexUniformDescriptorSet = true;
7580 } else if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_FRAGMENT) {
7581 commandBuffer->fragmentUniformBuffers[slotIndex] = uniformBuffer;
7582 commandBuffer->needNewFragmentUniformDescriptorSet = true;
7583 } else if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_COMPUTE) {
7584 commandBuffer->computeUniformBuffers[slotIndex] = uniformBuffer;
7585 commandBuffer->needNewComputeUniformDescriptorSet = true;
7586 } else {
7587 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!");
7588 return;
7589 }
7590 }
7591
7592 uniformBuffer->drawOffset = uniformBuffer->writeOffset;
7593
7594 Uint8 *dst =
7595 uniformBuffer->buffer->usedRegion->allocation->mapPointer +
7596 uniformBuffer->buffer->usedRegion->resourceOffset +
7597 uniformBuffer->writeOffset;
7598
7599 SDL_memcpy(
7600 dst,
7601 data,
7602 length);
7603
7604 uniformBuffer->writeOffset += blockSize;
7605
7606 if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_VERTEX) {
7607 commandBuffer->needNewVertexUniformOffsets = true;
7608 } else if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_FRAGMENT) {
7609 commandBuffer->needNewFragmentUniformOffsets = true;
7610 } else if (uniformBufferStage == VULKAN_UNIFORM_BUFFER_STAGE_COMPUTE) {
7611 commandBuffer->needNewComputeUniformOffsets = true;
7612 } else {
7613 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!");
7614 return;
7615 }
7616}
7617
7618static void VULKAN_BeginRenderPass(
7619 SDL_GPUCommandBuffer *commandBuffer,
7620 const SDL_GPUColorTargetInfo *colorTargetInfos,
7621 Uint32 numColorTargets,
7622 const SDL_GPUDepthStencilTargetInfo *depthStencilTargetInfo)
7623{
7624 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
7625 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
7626 VkRenderPass renderPass;
7627 VulkanFramebuffer *framebuffer;
7628
7629 Uint32 w, h;
7630 VkClearValue *clearValues;
7631 Uint32 clearCount = 0;
7632 Uint32 totalColorAttachmentCount = 0;
7633 Uint32 i;
7634 SDL_GPUViewport defaultViewport;
7635 SDL_Rect defaultScissor;
7636 SDL_FColor defaultBlendConstants;
7637 Uint32 framebufferWidth = SDL_MAX_UINT32;
7638 Uint32 framebufferHeight = SDL_MAX_UINT32;
7639
7640 for (i = 0; i < numColorTargets; i += 1) {
7641 VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)colorTargetInfos[i].texture;
7642
7643 w = textureContainer->header.info.width >> colorTargetInfos[i].mip_level;
7644 h = textureContainer->header.info.height >> colorTargetInfos[i].mip_level;
7645
7646 // The framebuffer cannot be larger than the smallest attachment.
7647
7648 if (w < framebufferWidth) {
7649 framebufferWidth = w;
7650 }
7651
7652 if (h < framebufferHeight) {
7653 framebufferHeight = h;
7654 }
7655 }
7656
7657 if (depthStencilTargetInfo != NULL) {
7658 VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)depthStencilTargetInfo->texture;
7659
7660 w = textureContainer->header.info.width;
7661 h = textureContainer->header.info.height;
7662
7663 // The framebuffer cannot be larger than the smallest attachment.
7664
7665 if (w < framebufferWidth) {
7666 framebufferWidth = w;
7667 }
7668
7669 if (h < framebufferHeight) {
7670 framebufferHeight = h;
7671 }
7672 }
7673
7674 for (i = 0; i < numColorTargets; i += 1) {
7675 VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)colorTargetInfos[i].texture;
7676 VulkanTextureSubresource *subresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
7677 renderer,
7678 vulkanCommandBuffer,
7679 textureContainer,
7680 textureContainer->header.info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : colorTargetInfos[i].layer_or_depth_plane,
7681 colorTargetInfos[i].mip_level,
7682 colorTargetInfos[i].cycle,
7683 VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT);
7684
7685 vulkanCommandBuffer->colorAttachmentSubresources[vulkanCommandBuffer->colorAttachmentSubresourceCount] = subresource;
7686 vulkanCommandBuffer->colorAttachmentSubresourceCount += 1;
7687 VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, subresource->parent);
7688 totalColorAttachmentCount += 1;
7689 clearCount += 1;
7690
7691 if (colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE || colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) {
7692 VulkanTextureContainer *resolveContainer = (VulkanTextureContainer *)colorTargetInfos[i].resolve_texture;
7693 VulkanTextureSubresource *resolveSubresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
7694 renderer,
7695 vulkanCommandBuffer,
7696 resolveContainer,
7697 colorTargetInfos[i].resolve_layer,
7698 colorTargetInfos[i].resolve_mip_level,
7699 colorTargetInfos[i].cycle_resolve_texture,
7700 VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT);
7701
7702 vulkanCommandBuffer->resolveAttachmentSubresources[vulkanCommandBuffer->resolveAttachmentSubresourceCount] = resolveSubresource;
7703 vulkanCommandBuffer->resolveAttachmentSubresourceCount += 1;
7704 VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, resolveSubresource->parent);
7705 totalColorAttachmentCount += 1;
7706 clearCount += 1;
7707 }
7708 }
7709
7710 if (depthStencilTargetInfo != NULL) {
7711 VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)depthStencilTargetInfo->texture;
7712 VulkanTextureSubresource *subresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
7713 renderer,
7714 vulkanCommandBuffer,
7715 textureContainer,
7716 0,
7717 0,
7718 depthStencilTargetInfo->cycle,
7719 VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT);
7720
7721 vulkanCommandBuffer->depthStencilAttachmentSubresource = subresource;
7722 VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, subresource->parent);
7723 clearCount += 1;
7724 }
7725
7726 // Fetch required render objects
7727
7728 renderPass = VULKAN_INTERNAL_FetchRenderPass(
7729 renderer,
7730 colorTargetInfos,
7731 numColorTargets,
7732 depthStencilTargetInfo);
7733
7734 if (renderPass == VK_NULL_HANDLE) {
7735 return;
7736 }
7737
7738 framebuffer = VULKAN_INTERNAL_FetchFramebuffer(
7739 renderer,
7740 renderPass,
7741 colorTargetInfos,
7742 numColorTargets,
7743 depthStencilTargetInfo,
7744 framebufferWidth,
7745 framebufferHeight);
7746
7747 if (framebuffer == NULL) {
7748 return;
7749 }
7750
7751 VULKAN_INTERNAL_TrackFramebuffer(vulkanCommandBuffer, framebuffer);
7752
7753 // Set clear values
7754
7755 clearValues = SDL_stack_alloc(VkClearValue, clearCount);
7756
7757 for (i = 0; i < totalColorAttachmentCount; i += 1) {
7758 clearValues[i].color.float32[0] = colorTargetInfos[i].clear_color.r;
7759 clearValues[i].color.float32[1] = colorTargetInfos[i].clear_color.g;
7760 clearValues[i].color.float32[2] = colorTargetInfos[i].clear_color.b;
7761 clearValues[i].color.float32[3] = colorTargetInfos[i].clear_color.a;
7762
7763 if (colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE || colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) {
7764 // Skip over the resolve texture, we're not clearing it
7765 i += 1;
7766 }
7767 }
7768
7769 if (depthStencilTargetInfo != NULL) {
7770 clearValues[totalColorAttachmentCount].depthStencil.depth =
7771 depthStencilTargetInfo->clear_depth;
7772 clearValues[totalColorAttachmentCount].depthStencil.stencil =
7773 depthStencilTargetInfo->clear_stencil;
7774 }
7775
7776 VkRenderPassBeginInfo renderPassBeginInfo;
7777 renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
7778 renderPassBeginInfo.pNext = NULL;
7779 renderPassBeginInfo.renderPass = renderPass;
7780 renderPassBeginInfo.framebuffer = framebuffer->framebuffer;
7781 renderPassBeginInfo.pClearValues = clearValues;
7782 renderPassBeginInfo.clearValueCount = clearCount;
7783 renderPassBeginInfo.renderArea.extent.width = framebufferWidth;
7784 renderPassBeginInfo.renderArea.extent.height = framebufferHeight;
7785 renderPassBeginInfo.renderArea.offset.x = 0;
7786 renderPassBeginInfo.renderArea.offset.y = 0;
7787
7788 renderer->vkCmdBeginRenderPass(
7789 vulkanCommandBuffer->commandBuffer,
7790 &renderPassBeginInfo,
7791 VK_SUBPASS_CONTENTS_INLINE);
7792
7793 SDL_stack_free(clearValues);
7794
7795 // Set sensible default states
7796
7797 defaultViewport.x = 0;
7798 defaultViewport.y = 0;
7799 defaultViewport.w = (float)framebufferWidth;
7800 defaultViewport.h = (float)framebufferHeight;
7801 defaultViewport.min_depth = 0;
7802 defaultViewport.max_depth = 1;
7803
7804 VULKAN_INTERNAL_SetCurrentViewport(
7805 vulkanCommandBuffer,
7806 &defaultViewport);
7807
7808 defaultScissor.x = 0;
7809 defaultScissor.y = 0;
7810 defaultScissor.w = (Sint32)framebufferWidth;
7811 defaultScissor.h = (Sint32)framebufferHeight;
7812
7813 VULKAN_INTERNAL_SetCurrentScissor(
7814 vulkanCommandBuffer,
7815 &defaultScissor);
7816
7817 defaultBlendConstants.r = 1.0f;
7818 defaultBlendConstants.g = 1.0f;
7819 defaultBlendConstants.b = 1.0f;
7820 defaultBlendConstants.a = 1.0f;
7821
7822 VULKAN_INTERNAL_SetCurrentBlendConstants(
7823 vulkanCommandBuffer,
7824 defaultBlendConstants);
7825
7826 VULKAN_INTERNAL_SetCurrentStencilReference(
7827 vulkanCommandBuffer,
7828 0);
7829}
7830
7831static void VULKAN_BindGraphicsPipeline(
7832 SDL_GPUCommandBuffer *commandBuffer,
7833 SDL_GPUGraphicsPipeline *graphicsPipeline)
7834{
7835 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
7836 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
7837 VulkanGraphicsPipeline *pipeline = (VulkanGraphicsPipeline *)graphicsPipeline;
7838
7839 renderer->vkCmdBindPipeline(
7840 vulkanCommandBuffer->commandBuffer,
7841 VK_PIPELINE_BIND_POINT_GRAPHICS,
7842 pipeline->pipeline);
7843
7844 vulkanCommandBuffer->currentGraphicsPipeline = pipeline;
7845
7846 VULKAN_INTERNAL_TrackGraphicsPipeline(vulkanCommandBuffer, pipeline);
7847
7848 // Acquire uniform buffers if necessary
7849 for (Uint32 i = 0; i < pipeline->resourceLayout->vertexUniformBufferCount; i += 1) {
7850 if (vulkanCommandBuffer->vertexUniformBuffers[i] == NULL) {
7851 vulkanCommandBuffer->vertexUniformBuffers[i] = VULKAN_INTERNAL_AcquireUniformBufferFromPool(
7852 vulkanCommandBuffer);
7853 }
7854 }
7855
7856 for (Uint32 i = 0; i < pipeline->resourceLayout->fragmentUniformBufferCount; i += 1) {
7857 if (vulkanCommandBuffer->fragmentUniformBuffers[i] == NULL) {
7858 vulkanCommandBuffer->fragmentUniformBuffers[i] = VULKAN_INTERNAL_AcquireUniformBufferFromPool(
7859 vulkanCommandBuffer);
7860 }
7861 }
7862
7863 // Mark bindings as needed
7864 vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
7865 vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
7866 vulkanCommandBuffer->needNewVertexUniformDescriptorSet = true;
7867 vulkanCommandBuffer->needNewFragmentUniformDescriptorSet = true;
7868 vulkanCommandBuffer->needNewVertexUniformOffsets = true;
7869 vulkanCommandBuffer->needNewFragmentUniformOffsets = true;
7870}
7871
7872static void VULKAN_BindVertexBuffers(
7873 SDL_GPUCommandBuffer *commandBuffer,
7874 Uint32 firstSlot,
7875 const SDL_GPUBufferBinding *bindings,
7876 Uint32 numBindings)
7877{
7878 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
7879
7880 for (Uint32 i = 0; i < numBindings; i += 1) {
7881 VulkanBuffer *buffer = ((VulkanBufferContainer *)bindings[i].buffer)->activeBuffer;
7882 if (vulkanCommandBuffer->vertexBuffers[i] != buffer->buffer || vulkanCommandBuffer->vertexBufferOffsets[i] != bindings[i].offset) {
7883 VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, buffer);
7884
7885 vulkanCommandBuffer->vertexBuffers[i] = buffer->buffer;
7886 vulkanCommandBuffer->vertexBufferOffsets[i] = bindings[i].offset;
7887 vulkanCommandBuffer->needVertexBufferBind = true;
7888 }
7889 }
7890
7891 vulkanCommandBuffer->vertexBufferCount =
7892 SDL_max(vulkanCommandBuffer->vertexBufferCount, firstSlot + numBindings);
7893}
7894
7895static void VULKAN_BindIndexBuffer(
7896 SDL_GPUCommandBuffer *commandBuffer,
7897 const SDL_GPUBufferBinding *binding,
7898 SDL_GPUIndexElementSize indexElementSize)
7899{
7900 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
7901 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
7902 VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer *)binding->buffer)->activeBuffer;
7903
7904 VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, vulkanBuffer);
7905
7906 renderer->vkCmdBindIndexBuffer(
7907 vulkanCommandBuffer->commandBuffer,
7908 vulkanBuffer->buffer,
7909 (VkDeviceSize)binding->offset,
7910 SDLToVK_IndexType[indexElementSize]);
7911}
7912
7913static void VULKAN_PushVertexUniformData(
7914 SDL_GPUCommandBuffer *commandBuffer,
7915 Uint32 slotIndex,
7916 const void *data,
7917 Uint32 length)
7918{
7919 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
7920
7921 VULKAN_INTERNAL_PushUniformData(
7922 vulkanCommandBuffer,
7923 VULKAN_UNIFORM_BUFFER_STAGE_VERTEX,
7924 slotIndex,
7925 data,
7926 length);
7927}
7928
7929static void VULKAN_PushFragmentUniformData(
7930 SDL_GPUCommandBuffer *commandBuffer,
7931 Uint32 slotIndex,
7932 const void *data,
7933 Uint32 length)
7934{
7935 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
7936
7937 VULKAN_INTERNAL_PushUniformData(
7938 vulkanCommandBuffer,
7939 VULKAN_UNIFORM_BUFFER_STAGE_FRAGMENT,
7940 slotIndex,
7941 data,
7942 length);
7943}
7944
7945static void VULKAN_EndRenderPass(
7946 SDL_GPUCommandBuffer *commandBuffer)
7947{
7948 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
7949 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
7950 Uint32 i;
7951
7952 renderer->vkCmdEndRenderPass(
7953 vulkanCommandBuffer->commandBuffer);
7954
7955 for (i = 0; i < vulkanCommandBuffer->colorAttachmentSubresourceCount; i += 1) {
7956 VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
7957 renderer,
7958 vulkanCommandBuffer,
7959 VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT,
7960 vulkanCommandBuffer->colorAttachmentSubresources[i]);
7961 }
7962 vulkanCommandBuffer->colorAttachmentSubresourceCount = 0;
7963
7964 for (i = 0; i < vulkanCommandBuffer->resolveAttachmentSubresourceCount; i += 1) {
7965 VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
7966 renderer,
7967 vulkanCommandBuffer,
7968 VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT,
7969 vulkanCommandBuffer->resolveAttachmentSubresources[i]);
7970 }
7971 vulkanCommandBuffer->resolveAttachmentSubresourceCount = 0;
7972
7973 if (vulkanCommandBuffer->depthStencilAttachmentSubresource != NULL) {
7974 VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
7975 renderer,
7976 vulkanCommandBuffer,
7977 VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT,
7978 vulkanCommandBuffer->depthStencilAttachmentSubresource);
7979 vulkanCommandBuffer->depthStencilAttachmentSubresource = NULL;
7980 }
7981
7982 vulkanCommandBuffer->currentGraphicsPipeline = NULL;
7983
7984 vulkanCommandBuffer->vertexResourceDescriptorSet = VK_NULL_HANDLE;
7985 vulkanCommandBuffer->vertexUniformDescriptorSet = VK_NULL_HANDLE;
7986 vulkanCommandBuffer->fragmentResourceDescriptorSet = VK_NULL_HANDLE;
7987 vulkanCommandBuffer->fragmentUniformDescriptorSet = VK_NULL_HANDLE;
7988
7989 // Reset bind state
7990 SDL_zeroa(vulkanCommandBuffer->colorAttachmentSubresources);
7991 SDL_zeroa(vulkanCommandBuffer->resolveAttachmentSubresources);
7992 vulkanCommandBuffer->depthStencilAttachmentSubresource = NULL;
7993
7994 SDL_zeroa(vulkanCommandBuffer->vertexBuffers);
7995 SDL_zeroa(vulkanCommandBuffer->vertexBufferOffsets);
7996 vulkanCommandBuffer->vertexBufferCount = 0;
7997
7998 SDL_zeroa(vulkanCommandBuffer->vertexSamplers);
7999 SDL_zeroa(vulkanCommandBuffer->vertexSamplerTextures);
8000 SDL_zeroa(vulkanCommandBuffer->vertexStorageTextures);
8001 SDL_zeroa(vulkanCommandBuffer->vertexStorageBuffers);
8002
8003 SDL_zeroa(vulkanCommandBuffer->fragmentSamplers);
8004 SDL_zeroa(vulkanCommandBuffer->fragmentSamplerTextures);
8005 SDL_zeroa(vulkanCommandBuffer->fragmentStorageTextures);
8006 SDL_zeroa(vulkanCommandBuffer->fragmentStorageBuffers);
8007}
8008
8009static void VULKAN_BeginComputePass(
8010 SDL_GPUCommandBuffer *commandBuffer,
8011 const SDL_GPUStorageTextureReadWriteBinding *storageTextureBindings,
8012 Uint32 numStorageTextureBindings,
8013 const SDL_GPUStorageBufferReadWriteBinding *storageBufferBindings,
8014 Uint32 numStorageBufferBindings)
8015{
8016 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
8017 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
8018 VulkanBufferContainer *bufferContainer;
8019 VulkanBuffer *buffer;
8020 Uint32 i;
8021
8022 vulkanCommandBuffer->readWriteComputeStorageTextureSubresourceCount = numStorageTextureBindings;
8023
8024 for (i = 0; i < numStorageTextureBindings; i += 1) {
8025 VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)storageTextureBindings[i].texture;
8026 VulkanTextureSubresource *subresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
8027 renderer,
8028 vulkanCommandBuffer,
8029 textureContainer,
8030 storageTextureBindings[i].layer,
8031 storageTextureBindings[i].mip_level,
8032 storageTextureBindings[i].cycle,
8033 VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE);
8034
8035 vulkanCommandBuffer->readWriteComputeStorageTextureSubresources[i] = subresource;
8036
8037 VULKAN_INTERNAL_TrackTexture(
8038 vulkanCommandBuffer,
8039 subresource->parent);
8040 }
8041
8042 for (i = 0; i < numStorageBufferBindings; i += 1) {
8043 bufferContainer = (VulkanBufferContainer *)storageBufferBindings[i].buffer;
8044 buffer = VULKAN_INTERNAL_PrepareBufferForWrite(
8045 renderer,
8046 vulkanCommandBuffer,
8047 bufferContainer,
8048 storageBufferBindings[i].cycle,
8049 VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ);
8050
8051 vulkanCommandBuffer->readWriteComputeStorageBuffers[i] = buffer;
8052
8053 VULKAN_INTERNAL_TrackBuffer(
8054 vulkanCommandBuffer,
8055 buffer);
8056 }
8057}
8058
8059static void VULKAN_BindComputePipeline(
8060 SDL_GPUCommandBuffer *commandBuffer,
8061 SDL_GPUComputePipeline *computePipeline)
8062{
8063 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
8064 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
8065 VulkanComputePipeline *vulkanComputePipeline = (VulkanComputePipeline *)computePipeline;
8066
8067 renderer->vkCmdBindPipeline(
8068 vulkanCommandBuffer->commandBuffer,
8069 VK_PIPELINE_BIND_POINT_COMPUTE,
8070 vulkanComputePipeline->pipeline);
8071
8072 vulkanCommandBuffer->currentComputePipeline = vulkanComputePipeline;
8073
8074 VULKAN_INTERNAL_TrackComputePipeline(vulkanCommandBuffer, vulkanComputePipeline);
8075
8076 // Acquire uniform buffers if necessary
8077 for (Uint32 i = 0; i < vulkanComputePipeline->resourceLayout->numUniformBuffers; i += 1) {
8078 if (vulkanCommandBuffer->computeUniformBuffers[i] == NULL) {
8079 vulkanCommandBuffer->computeUniformBuffers[i] = VULKAN_INTERNAL_AcquireUniformBufferFromPool(
8080 vulkanCommandBuffer);
8081 }
8082 }
8083
8084 // Mark binding as needed
8085 vulkanCommandBuffer->needNewComputeReadWriteDescriptorSet = true;
8086 vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
8087 vulkanCommandBuffer->needNewComputeUniformDescriptorSet = true;
8088 vulkanCommandBuffer->needNewComputeUniformOffsets = true;
8089}
8090
8091static void VULKAN_BindComputeSamplers(
8092 SDL_GPUCommandBuffer *commandBuffer,
8093 Uint32 firstSlot,
8094 const SDL_GPUTextureSamplerBinding *textureSamplerBindings,
8095 Uint32 numBindings)
8096{
8097 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
8098
8099 for (Uint32 i = 0; i < numBindings; i += 1) {
8100 VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)textureSamplerBindings[i].texture;
8101 VulkanSampler *sampler = (VulkanSampler *)textureSamplerBindings[i].sampler;
8102
8103 if (vulkanCommandBuffer->computeSamplers[firstSlot + i] != sampler) {
8104 VULKAN_INTERNAL_TrackSampler(
8105 vulkanCommandBuffer,
8106 sampler);
8107
8108 vulkanCommandBuffer->computeSamplers[firstSlot + i] = sampler;
8109 vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
8110 }
8111
8112 if (vulkanCommandBuffer->computeSamplerTextures[firstSlot + i] != textureContainer->activeTexture) {
8113 VULKAN_INTERNAL_TrackTexture(
8114 vulkanCommandBuffer,
8115 textureContainer->activeTexture);
8116
8117 vulkanCommandBuffer->computeSamplerTextures[firstSlot + i] = textureContainer->activeTexture;
8118 vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
8119 }
8120 }
8121}
8122
8123static void VULKAN_BindComputeStorageTextures(
8124 SDL_GPUCommandBuffer *commandBuffer,
8125 Uint32 firstSlot,
8126 SDL_GPUTexture *const *storageTextures,
8127 Uint32 numBindings)
8128{
8129 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
8130 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
8131
8132 for (Uint32 i = 0; i < numBindings; i += 1) {
8133 VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)storageTextures[i];
8134
8135 if (vulkanCommandBuffer->readOnlyComputeStorageTextures[firstSlot + i] != textureContainer->activeTexture) {
8136 /* If a different texture as in this slot, transition it back to its default usage */
8137 if (vulkanCommandBuffer->readOnlyComputeStorageTextures[firstSlot + i] != NULL) {
8138 VULKAN_INTERNAL_TextureTransitionToDefaultUsage(
8139 renderer,
8140 vulkanCommandBuffer,
8141 VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ,
8142 vulkanCommandBuffer->readOnlyComputeStorageTextures[firstSlot + i]);
8143 }
8144
8145 /* Then transition the new texture and prepare it for binding */
8146 VULKAN_INTERNAL_TextureTransitionFromDefaultUsage(
8147 renderer,
8148 vulkanCommandBuffer,
8149 VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ,
8150 textureContainer->activeTexture);
8151
8152
8153 VULKAN_INTERNAL_TrackTexture(
8154 vulkanCommandBuffer,
8155 textureContainer->activeTexture);
8156
8157 vulkanCommandBuffer->readOnlyComputeStorageTextures[firstSlot + i] = textureContainer->activeTexture;
8158 vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
8159 }
8160 }
8161}
8162
8163static void VULKAN_BindComputeStorageBuffers(
8164 SDL_GPUCommandBuffer *commandBuffer,
8165 Uint32 firstSlot,
8166 SDL_GPUBuffer *const *storageBuffers,
8167 Uint32 numBindings)
8168{
8169 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
8170 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
8171
8172 for (Uint32 i = 0; i < numBindings; i += 1) {
8173 VulkanBufferContainer *bufferContainer = (VulkanBufferContainer *)storageBuffers[i];
8174
8175 if (vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i] != bufferContainer->activeBuffer) {
8176 /* If a different buffer was in this slot, transition it back to its default usage */
8177 if (vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i] != NULL) {
8178 VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
8179 renderer,
8180 vulkanCommandBuffer,
8181 VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ,
8182 vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i]);
8183 }
8184
8185 /* Then transition the new buffer and prepare it for binding */
8186 VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
8187 renderer,
8188 vulkanCommandBuffer,
8189 VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ,
8190 bufferContainer->activeBuffer);
8191
8192 VULKAN_INTERNAL_TrackBuffer(
8193 vulkanCommandBuffer,
8194 bufferContainer->activeBuffer);
8195
8196 vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i] = bufferContainer->activeBuffer;
8197 vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
8198 }
8199 }
8200}
8201
8202static void VULKAN_PushComputeUniformData(
8203 SDL_GPUCommandBuffer *commandBuffer,
8204 Uint32 slotIndex,
8205 const void *data,
8206 Uint32 length)
8207{
8208 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
8209
8210 VULKAN_INTERNAL_PushUniformData(
8211 vulkanCommandBuffer,
8212 VULKAN_UNIFORM_BUFFER_STAGE_COMPUTE,
8213 slotIndex,
8214 data,
8215 length);
8216}
8217
8218static void VULKAN_INTERNAL_BindComputeDescriptorSets(
8219 VulkanRenderer *renderer,
8220 VulkanCommandBuffer *commandBuffer)
8221{
8222 VulkanComputePipelineResourceLayout *resourceLayout;
8223 DescriptorSetLayout *descriptorSetLayout;
8224 VkWriteDescriptorSet writeDescriptorSets[
8225 MAX_TEXTURE_SAMPLERS_PER_STAGE +
8226 MAX_STORAGE_TEXTURES_PER_STAGE +
8227 MAX_STORAGE_BUFFERS_PER_STAGE +
8228 MAX_COMPUTE_WRITE_TEXTURES +
8229 MAX_COMPUTE_WRITE_BUFFERS +
8230 MAX_UNIFORM_BUFFERS_PER_STAGE];
8231 VkDescriptorBufferInfo bufferInfos[MAX_STORAGE_BUFFERS_PER_STAGE + MAX_COMPUTE_WRITE_BUFFERS + MAX_UNIFORM_BUFFERS_PER_STAGE];
8232 VkDescriptorImageInfo imageInfos[MAX_TEXTURE_SAMPLERS_PER_STAGE + MAX_STORAGE_TEXTURES_PER_STAGE + MAX_COMPUTE_WRITE_TEXTURES];
8233 Uint32 dynamicOffsets[MAX_UNIFORM_BUFFERS_PER_STAGE];
8234 Uint32 writeCount = 0;
8235 Uint32 bufferInfoCount = 0;
8236 Uint32 imageInfoCount = 0;
8237 Uint32 dynamicOffsetCount = 0;
8238
8239 if (
8240 !commandBuffer->needNewComputeReadOnlyDescriptorSet &&
8241 !commandBuffer->needNewComputeReadWriteDescriptorSet &&
8242 !commandBuffer->needNewComputeUniformDescriptorSet &&
8243 !commandBuffer->needNewComputeUniformOffsets
8244 ) {
8245 return;
8246 }
8247
8248 resourceLayout = commandBuffer->currentComputePipeline->resourceLayout;
8249
8250 if (commandBuffer->needNewComputeReadOnlyDescriptorSet) {
8251 descriptorSetLayout = resourceLayout->descriptorSetLayouts[0];
8252
8253 commandBuffer->computeReadOnlyDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet(
8254 renderer,
8255 commandBuffer,
8256 descriptorSetLayout);
8257
8258 for (Uint32 i = 0; i < resourceLayout->numSamplers; i += 1) {
8259 VkWriteDescriptorSet *currentWriteDescriptorSet = &writeDescriptorSets[writeCount];
8260
8261 currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
8262 currentWriteDescriptorSet->pNext = NULL;
8263 currentWriteDescriptorSet->descriptorCount = 1;
8264 currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
8265 currentWriteDescriptorSet->dstArrayElement = 0;
8266 currentWriteDescriptorSet->dstBinding = i;
8267 currentWriteDescriptorSet->dstSet = commandBuffer->computeReadOnlyDescriptorSet;
8268 currentWriteDescriptorSet->pTexelBufferView = NULL;
8269 currentWriteDescriptorSet->pBufferInfo = NULL;
8270
8271 imageInfos[imageInfoCount].sampler = commandBuffer->computeSamplers[i]->sampler;
8272 imageInfos[imageInfoCount].imageView = commandBuffer->computeSamplerTextures[i]->fullView;
8273 imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
8274
8275 currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount];
8276
8277 writeCount += 1;
8278 imageInfoCount += 1;
8279 }
8280
8281 for (Uint32 i = 0; i < resourceLayout->numReadonlyStorageTextures; i += 1) {
8282 VkWriteDescriptorSet *currentWriteDescriptorSet = &writeDescriptorSets[writeCount];
8283
8284 currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
8285 currentWriteDescriptorSet->pNext = NULL;
8286 currentWriteDescriptorSet->descriptorCount = 1;
8287 currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; // Yes, we are declaring the readonly storage texture as a sampled image, because shaders are stupid.
8288 currentWriteDescriptorSet->dstArrayElement = 0;
8289 currentWriteDescriptorSet->dstBinding = resourceLayout->numSamplers + i;
8290 currentWriteDescriptorSet->dstSet = commandBuffer->computeReadOnlyDescriptorSet;
8291 currentWriteDescriptorSet->pTexelBufferView = NULL;
8292 currentWriteDescriptorSet->pBufferInfo = NULL;
8293
8294 imageInfos[imageInfoCount].sampler = VK_NULL_HANDLE;
8295 imageInfos[imageInfoCount].imageView = commandBuffer->readOnlyComputeStorageTextures[i]->fullView;
8296 imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
8297
8298 currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount];
8299
8300 writeCount += 1;
8301 imageInfoCount += 1;
8302 }
8303
8304 for (Uint32 i = 0; i < resourceLayout->numReadonlyStorageBuffers; i += 1) {
8305 VkWriteDescriptorSet *currentWriteDescriptorSet = &writeDescriptorSets[writeCount];
8306
8307 currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
8308 currentWriteDescriptorSet->pNext = NULL;
8309 currentWriteDescriptorSet->descriptorCount = 1;
8310 currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
8311 currentWriteDescriptorSet->dstArrayElement = 0;
8312 currentWriteDescriptorSet->dstBinding = resourceLayout->numSamplers + resourceLayout->numReadonlyStorageTextures + i;
8313 currentWriteDescriptorSet->dstSet = commandBuffer->computeReadOnlyDescriptorSet;
8314 currentWriteDescriptorSet->pTexelBufferView = NULL;
8315 currentWriteDescriptorSet->pImageInfo = NULL;
8316
8317 bufferInfos[bufferInfoCount].buffer = commandBuffer->readOnlyComputeStorageBuffers[i]->buffer;
8318 bufferInfos[bufferInfoCount].offset = 0;
8319 bufferInfos[bufferInfoCount].range = VK_WHOLE_SIZE;
8320
8321 currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount];
8322
8323 writeCount += 1;
8324 bufferInfoCount += 1;
8325 }
8326
8327 commandBuffer->needNewComputeReadOnlyDescriptorSet = false;
8328 }
8329
8330 if (commandBuffer->needNewComputeReadWriteDescriptorSet) {
8331 descriptorSetLayout = resourceLayout->descriptorSetLayouts[1];
8332
8333 commandBuffer->computeReadWriteDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet(
8334 renderer,
8335 commandBuffer,
8336 descriptorSetLayout);
8337
8338 for (Uint32 i = 0; i < resourceLayout->numReadWriteStorageTextures; i += 1) {
8339 VkWriteDescriptorSet *currentWriteDescriptorSet = &writeDescriptorSets[writeCount];
8340
8341 currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
8342 currentWriteDescriptorSet->pNext = NULL;
8343 currentWriteDescriptorSet->descriptorCount = 1;
8344 currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
8345 currentWriteDescriptorSet->dstArrayElement = 0;
8346 currentWriteDescriptorSet->dstBinding = i;
8347 currentWriteDescriptorSet->dstSet = commandBuffer->computeReadWriteDescriptorSet;
8348 currentWriteDescriptorSet->pTexelBufferView = NULL;
8349 currentWriteDescriptorSet->pBufferInfo = NULL;
8350
8351 imageInfos[imageInfoCount].sampler = VK_NULL_HANDLE;
8352 imageInfos[imageInfoCount].imageView = commandBuffer->readWriteComputeStorageTextureSubresources[i]->computeWriteView;
8353 imageInfos[imageInfoCount].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
8354
8355 currentWriteDescriptorSet->pImageInfo = &imageInfos[imageInfoCount];
8356
8357 writeCount += 1;
8358 imageInfoCount += 1;
8359 }
8360
8361 for (Uint32 i = 0; i < resourceLayout->numReadWriteStorageBuffers; i += 1) {
8362 VkWriteDescriptorSet *currentWriteDescriptorSet = &writeDescriptorSets[writeCount];
8363
8364 currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
8365 currentWriteDescriptorSet->pNext = NULL;
8366 currentWriteDescriptorSet->descriptorCount = 1;
8367 currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
8368 currentWriteDescriptorSet->dstArrayElement = 0;
8369 currentWriteDescriptorSet->dstBinding = resourceLayout->numReadWriteStorageTextures + i;
8370 currentWriteDescriptorSet->dstSet = commandBuffer->computeReadWriteDescriptorSet;
8371 currentWriteDescriptorSet->pTexelBufferView = NULL;
8372 currentWriteDescriptorSet->pImageInfo = NULL;
8373
8374 bufferInfos[bufferInfoCount].buffer = commandBuffer->readWriteComputeStorageBuffers[i]->buffer;
8375 bufferInfos[bufferInfoCount].offset = 0;
8376 bufferInfos[bufferInfoCount].range = VK_WHOLE_SIZE;
8377
8378 currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount];
8379
8380 writeCount += 1;
8381 bufferInfoCount += 1;
8382 }
8383
8384 commandBuffer->needNewComputeReadWriteDescriptorSet = false;
8385 }
8386
8387 if (commandBuffer->needNewComputeUniformDescriptorSet) {
8388 descriptorSetLayout = resourceLayout->descriptorSetLayouts[2];
8389
8390 commandBuffer->computeUniformDescriptorSet = VULKAN_INTERNAL_FetchDescriptorSet(
8391 renderer,
8392 commandBuffer,
8393 descriptorSetLayout);
8394
8395
8396 for (Uint32 i = 0; i < resourceLayout->numUniformBuffers; i += 1) {
8397 VkWriteDescriptorSet *currentWriteDescriptorSet = &writeDescriptorSets[writeCount];
8398
8399 currentWriteDescriptorSet->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
8400 currentWriteDescriptorSet->pNext = NULL;
8401 currentWriteDescriptorSet->descriptorCount = 1;
8402 currentWriteDescriptorSet->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
8403 currentWriteDescriptorSet->dstArrayElement = 0;
8404 currentWriteDescriptorSet->dstBinding = i;
8405 currentWriteDescriptorSet->dstSet = commandBuffer->computeUniformDescriptorSet;
8406 currentWriteDescriptorSet->pTexelBufferView = NULL;
8407 currentWriteDescriptorSet->pImageInfo = NULL;
8408
8409 bufferInfos[bufferInfoCount].buffer = commandBuffer->computeUniformBuffers[i]->buffer->buffer;
8410 bufferInfos[bufferInfoCount].offset = 0;
8411 bufferInfos[bufferInfoCount].range = MAX_UBO_SECTION_SIZE;
8412
8413 currentWriteDescriptorSet->pBufferInfo = &bufferInfos[bufferInfoCount];
8414
8415 writeCount += 1;
8416 bufferInfoCount += 1;
8417 }
8418
8419 commandBuffer->needNewComputeUniformDescriptorSet = false;
8420 }
8421
8422 for (Uint32 i = 0; i < resourceLayout->numUniformBuffers; i += 1) {
8423 dynamicOffsets[i] = commandBuffer->computeUniformBuffers[i]->drawOffset;
8424 dynamicOffsetCount += 1;
8425 }
8426
8427 renderer->vkUpdateDescriptorSets(
8428 renderer->logicalDevice,
8429 writeCount,
8430 writeDescriptorSets,
8431 0,
8432 NULL);
8433
8434 VkDescriptorSet sets[3];
8435 sets[0] = commandBuffer->computeReadOnlyDescriptorSet;
8436 sets[1] = commandBuffer->computeReadWriteDescriptorSet;
8437 sets[2] = commandBuffer->computeUniformDescriptorSet;
8438
8439 renderer->vkCmdBindDescriptorSets(
8440 commandBuffer->commandBuffer,
8441 VK_PIPELINE_BIND_POINT_COMPUTE,
8442 resourceLayout->pipelineLayout,
8443 0,
8444 3,
8445 sets,
8446 dynamicOffsetCount,
8447 dynamicOffsets);
8448
8449 commandBuffer->needNewVertexUniformOffsets = false;
8450}
8451
8452static void VULKAN_DispatchCompute(
8453 SDL_GPUCommandBuffer *commandBuffer,
8454 Uint32 groupcountX,
8455 Uint32 groupcountY,
8456 Uint32 groupcountZ)
8457{
8458 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
8459 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
8460
8461 VULKAN_INTERNAL_BindComputeDescriptorSets(renderer, vulkanCommandBuffer);
8462
8463 renderer->vkCmdDispatch(
8464 vulkanCommandBuffer->commandBuffer,
8465 groupcountX,
8466 groupcountY,
8467 groupcountZ);
8468}
8469
8470static void VULKAN_DispatchComputeIndirect(
8471 SDL_GPUCommandBuffer *commandBuffer,
8472 SDL_GPUBuffer *buffer,
8473 Uint32 offset)
8474{
8475 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
8476 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
8477 VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer *)buffer)->activeBuffer;
8478
8479 VULKAN_INTERNAL_BindComputeDescriptorSets(renderer, vulkanCommandBuffer);
8480
8481 renderer->vkCmdDispatchIndirect(
8482 vulkanCommandBuffer->commandBuffer,
8483 vulkanBuffer->buffer,
8484 offset);
8485
8486 VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, vulkanBuffer);
8487}
8488
8489static void VULKAN_EndComputePass(
8490 SDL_GPUCommandBuffer *commandBuffer)
8491{
8492 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
8493 Uint32 i;
8494
8495 for (i = 0; i < vulkanCommandBuffer->readWriteComputeStorageTextureSubresourceCount; i += 1) {
8496 VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
8497 vulkanCommandBuffer->renderer,
8498 vulkanCommandBuffer,
8499 VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE,
8500 vulkanCommandBuffer->readWriteComputeStorageTextureSubresources[i]);
8501 vulkanCommandBuffer->readWriteComputeStorageTextureSubresources[i] = NULL;
8502 }
8503 vulkanCommandBuffer->readWriteComputeStorageTextureSubresourceCount = 0;
8504
8505 for (i = 0; i < MAX_COMPUTE_WRITE_BUFFERS; i += 1) {
8506 if (vulkanCommandBuffer->readWriteComputeStorageBuffers[i] != NULL) {
8507 VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
8508 vulkanCommandBuffer->renderer,
8509 vulkanCommandBuffer,
8510 VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE,
8511 vulkanCommandBuffer->readWriteComputeStorageBuffers[i]);
8512
8513 vulkanCommandBuffer->readWriteComputeStorageBuffers[i] = NULL;
8514 }
8515 }
8516
8517 for (i = 0; i < MAX_STORAGE_TEXTURES_PER_STAGE; i += 1) {
8518 if (vulkanCommandBuffer->readOnlyComputeStorageTextures[i] != NULL) {
8519 VULKAN_INTERNAL_TextureTransitionToDefaultUsage(
8520 vulkanCommandBuffer->renderer,
8521 vulkanCommandBuffer,
8522 VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ,
8523 vulkanCommandBuffer->readOnlyComputeStorageTextures[i]);
8524
8525 vulkanCommandBuffer->readOnlyComputeStorageTextures[i] = NULL;
8526 }
8527 }
8528
8529 for (i = 0; i < MAX_STORAGE_BUFFERS_PER_STAGE; i += 1) {
8530 if (vulkanCommandBuffer->readOnlyComputeStorageBuffers[i] != NULL) {
8531 VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
8532 vulkanCommandBuffer->renderer,
8533 vulkanCommandBuffer,
8534 VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ,
8535 vulkanCommandBuffer->readOnlyComputeStorageBuffers[i]);
8536
8537 vulkanCommandBuffer->readOnlyComputeStorageBuffers[i] = NULL;
8538 }
8539 }
8540
8541 // we don't need a barrier because sampler state is always the default if sampler bit is set
8542 SDL_zeroa(vulkanCommandBuffer->computeSamplerTextures);
8543 SDL_zeroa(vulkanCommandBuffer->computeSamplers);
8544
8545 vulkanCommandBuffer->currentComputePipeline = NULL;
8546
8547 vulkanCommandBuffer->computeReadOnlyDescriptorSet = VK_NULL_HANDLE;
8548 vulkanCommandBuffer->computeReadWriteDescriptorSet = VK_NULL_HANDLE;
8549 vulkanCommandBuffer->computeUniformDescriptorSet = VK_NULL_HANDLE;
8550}
8551
8552static void *VULKAN_MapTransferBuffer(
8553 SDL_GPURenderer *driverData,
8554 SDL_GPUTransferBuffer *transferBuffer,
8555 bool cycle)
8556{
8557 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
8558 VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer *)transferBuffer;
8559
8560 if (
8561 cycle &&
8562 SDL_GetAtomicInt(&transferBufferContainer->activeBuffer->referenceCount) > 0) {
8563 VULKAN_INTERNAL_CycleActiveBuffer(
8564 renderer,
8565 transferBufferContainer);
8566 }
8567
8568 Uint8 *bufferPointer =
8569 transferBufferContainer->activeBuffer->usedRegion->allocation->mapPointer +
8570 transferBufferContainer->activeBuffer->usedRegion->resourceOffset;
8571
8572 return bufferPointer;
8573}
8574
8575static void VULKAN_UnmapTransferBuffer(
8576 SDL_GPURenderer *driverData,
8577 SDL_GPUTransferBuffer *transferBuffer)
8578{
8579 // no-op because transfer buffers are persistently mapped
8580 (void)driverData;
8581 (void)transferBuffer;
8582}
8583
8584static void VULKAN_BeginCopyPass(
8585 SDL_GPUCommandBuffer *commandBuffer)
8586{
8587 // no-op
8588 (void)commandBuffer;
8589}
8590
8591static void VULKAN_UploadToTexture(
8592 SDL_GPUCommandBuffer *commandBuffer,
8593 const SDL_GPUTextureTransferInfo *source,
8594 const SDL_GPUTextureRegion *destination,
8595 bool cycle)
8596{
8597 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
8598 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
8599 VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer *)source->transfer_buffer;
8600 VulkanTextureContainer *vulkanTextureContainer = (VulkanTextureContainer *)destination->texture;
8601 VulkanTextureSubresource *vulkanTextureSubresource;
8602 VkBufferImageCopy imageCopy;
8603
8604 // Note that the transfer buffer does not need a barrier, as it is synced by the client
8605
8606 vulkanTextureSubresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
8607 renderer,
8608 vulkanCommandBuffer,
8609 vulkanTextureContainer,
8610 destination->layer,
8611 destination->mip_level,
8612 cycle,
8613 VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION);
8614
8615 imageCopy.imageExtent.width = destination->w;
8616 imageCopy.imageExtent.height = destination->h;
8617 imageCopy.imageExtent.depth = destination->d;
8618 imageCopy.imageOffset.x = destination->x;
8619 imageCopy.imageOffset.y = destination->y;
8620 imageCopy.imageOffset.z = destination->z;
8621 imageCopy.imageSubresource.aspectMask = vulkanTextureSubresource->parent->aspectFlags;
8622 imageCopy.imageSubresource.baseArrayLayer = destination->layer;
8623 imageCopy.imageSubresource.layerCount = 1;
8624 imageCopy.imageSubresource.mipLevel = destination->mip_level;
8625 imageCopy.bufferOffset = source->offset;
8626 imageCopy.bufferRowLength = source->pixels_per_row;
8627 imageCopy.bufferImageHeight = source->rows_per_layer;
8628
8629 renderer->vkCmdCopyBufferToImage(
8630 vulkanCommandBuffer->commandBuffer,
8631 transferBufferContainer->activeBuffer->buffer,
8632 vulkanTextureSubresource->parent->image,
8633 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
8634 1,
8635 &imageCopy);
8636
8637 VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
8638 renderer,
8639 vulkanCommandBuffer,
8640 VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
8641 vulkanTextureSubresource);
8642
8643 VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBuffer);
8644 VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, vulkanTextureSubresource->parent);
8645}
8646
8647static void VULKAN_UploadToBuffer(
8648 SDL_GPUCommandBuffer *commandBuffer,
8649 const SDL_GPUTransferBufferLocation *source,
8650 const SDL_GPUBufferRegion *destination,
8651 bool cycle)
8652{
8653 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
8654 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
8655 VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer *)source->transfer_buffer;
8656 VulkanBufferContainer *bufferContainer = (VulkanBufferContainer *)destination->buffer;
8657 VkBufferCopy bufferCopy;
8658
8659 // Note that the transfer buffer does not need a barrier, as it is synced by the client
8660
8661 VulkanBuffer *vulkanBuffer = VULKAN_INTERNAL_PrepareBufferForWrite(
8662 renderer,
8663 vulkanCommandBuffer,
8664 bufferContainer,
8665 cycle,
8666 VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION);
8667
8668 bufferCopy.srcOffset = source->offset;
8669 bufferCopy.dstOffset = destination->offset;
8670 bufferCopy.size = destination->size;
8671
8672 renderer->vkCmdCopyBuffer(
8673 vulkanCommandBuffer->commandBuffer,
8674 transferBufferContainer->activeBuffer->buffer,
8675 vulkanBuffer->buffer,
8676 1,
8677 &bufferCopy);
8678
8679 VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
8680 renderer,
8681 vulkanCommandBuffer,
8682 VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION,
8683 vulkanBuffer);
8684
8685 VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBuffer);
8686 VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, vulkanBuffer);
8687}
8688
8689// Readback
8690
8691static void VULKAN_DownloadFromTexture(
8692 SDL_GPUCommandBuffer *commandBuffer,
8693 const SDL_GPUTextureRegion *source,
8694 const SDL_GPUTextureTransferInfo *destination)
8695{
8696 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
8697 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
8698 VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)source->texture;
8699 VulkanTextureSubresource *vulkanTextureSubresource;
8700 VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer *)destination->transfer_buffer;
8701 VkBufferImageCopy imageCopy;
8702 vulkanTextureSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
8703 textureContainer,
8704 source->layer,
8705 source->mip_level);
8706
8707 // Note that the transfer buffer does not need a barrier, as it is synced by the client
8708
8709 VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
8710 renderer,
8711 vulkanCommandBuffer,
8712 VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
8713 vulkanTextureSubresource);
8714
8715 imageCopy.imageExtent.width = source->w;
8716 imageCopy.imageExtent.height = source->h;
8717 imageCopy.imageExtent.depth = source->d;
8718 imageCopy.imageOffset.x = source->x;
8719 imageCopy.imageOffset.y = source->y;
8720 imageCopy.imageOffset.z = source->z;
8721 imageCopy.imageSubresource.aspectMask = vulkanTextureSubresource->parent->aspectFlags;
8722 imageCopy.imageSubresource.baseArrayLayer = source->layer;
8723 imageCopy.imageSubresource.layerCount = 1;
8724 imageCopy.imageSubresource.mipLevel = source->mip_level;
8725 imageCopy.bufferOffset = destination->offset;
8726 imageCopy.bufferRowLength = destination->pixels_per_row;
8727 imageCopy.bufferImageHeight = destination->rows_per_layer;
8728
8729 renderer->vkCmdCopyImageToBuffer(
8730 vulkanCommandBuffer->commandBuffer,
8731 vulkanTextureSubresource->parent->image,
8732 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
8733 transferBufferContainer->activeBuffer->buffer,
8734 1,
8735 &imageCopy);
8736
8737 VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
8738 renderer,
8739 vulkanCommandBuffer,
8740 VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
8741 vulkanTextureSubresource);
8742
8743 VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBuffer);
8744 VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, vulkanTextureSubresource->parent);
8745}
8746
8747static void VULKAN_DownloadFromBuffer(
8748 SDL_GPUCommandBuffer *commandBuffer,
8749 const SDL_GPUBufferRegion *source,
8750 const SDL_GPUTransferBufferLocation *destination)
8751{
8752 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
8753 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
8754 VulkanBufferContainer *bufferContainer = (VulkanBufferContainer *)source->buffer;
8755 VulkanBufferContainer *transferBufferContainer = (VulkanBufferContainer *)destination->transfer_buffer;
8756 VkBufferCopy bufferCopy;
8757
8758 // Note that transfer buffer does not need a barrier, as it is synced by the client
8759
8760 VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
8761 renderer,
8762 vulkanCommandBuffer,
8763 VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE,
8764 bufferContainer->activeBuffer);
8765
8766 bufferCopy.srcOffset = source->offset;
8767 bufferCopy.dstOffset = destination->offset;
8768 bufferCopy.size = source->size;
8769
8770 renderer->vkCmdCopyBuffer(
8771 vulkanCommandBuffer->commandBuffer,
8772 bufferContainer->activeBuffer->buffer,
8773 transferBufferContainer->activeBuffer->buffer,
8774 1,
8775 &bufferCopy);
8776
8777 VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
8778 renderer,
8779 vulkanCommandBuffer,
8780 VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE,
8781 bufferContainer->activeBuffer);
8782
8783 VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBuffer);
8784 VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, bufferContainer->activeBuffer);
8785}
8786
8787static void VULKAN_CopyTextureToTexture(
8788 SDL_GPUCommandBuffer *commandBuffer,
8789 const SDL_GPUTextureLocation *source,
8790 const SDL_GPUTextureLocation *destination,
8791 Uint32 w,
8792 Uint32 h,
8793 Uint32 d,
8794 bool cycle)
8795{
8796 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
8797 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
8798 VulkanTextureSubresource *srcSubresource;
8799 VulkanTextureSubresource *dstSubresource;
8800 VkImageCopy imageCopy;
8801
8802 srcSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
8803 (VulkanTextureContainer *)source->texture,
8804 source->layer,
8805 source->mip_level);
8806
8807 dstSubresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
8808 renderer,
8809 vulkanCommandBuffer,
8810 (VulkanTextureContainer *)destination->texture,
8811 destination->layer,
8812 destination->mip_level,
8813 cycle,
8814 VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION);
8815
8816 VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
8817 renderer,
8818 vulkanCommandBuffer,
8819 VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
8820 srcSubresource);
8821
8822 imageCopy.srcOffset.x = source->x;
8823 imageCopy.srcOffset.y = source->y;
8824 imageCopy.srcOffset.z = source->z;
8825 imageCopy.srcSubresource.aspectMask = srcSubresource->parent->aspectFlags;
8826 imageCopy.srcSubresource.baseArrayLayer = source->layer;
8827 imageCopy.srcSubresource.layerCount = 1;
8828 imageCopy.srcSubresource.mipLevel = source->mip_level;
8829 imageCopy.dstOffset.x = destination->x;
8830 imageCopy.dstOffset.y = destination->y;
8831 imageCopy.dstOffset.z = destination->z;
8832 imageCopy.dstSubresource.aspectMask = dstSubresource->parent->aspectFlags;
8833 imageCopy.dstSubresource.baseArrayLayer = destination->layer;
8834 imageCopy.dstSubresource.layerCount = 1;
8835 imageCopy.dstSubresource.mipLevel = destination->mip_level;
8836 imageCopy.extent.width = w;
8837 imageCopy.extent.height = h;
8838 imageCopy.extent.depth = d;
8839
8840 renderer->vkCmdCopyImage(
8841 vulkanCommandBuffer->commandBuffer,
8842 srcSubresource->parent->image,
8843 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
8844 dstSubresource->parent->image,
8845 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
8846 1,
8847 &imageCopy);
8848
8849 VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
8850 renderer,
8851 vulkanCommandBuffer,
8852 VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
8853 srcSubresource);
8854
8855 VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
8856 renderer,
8857 vulkanCommandBuffer,
8858 VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
8859 dstSubresource);
8860
8861 VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, srcSubresource->parent);
8862 VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, dstSubresource->parent);
8863}
8864
8865static void VULKAN_CopyBufferToBuffer(
8866 SDL_GPUCommandBuffer *commandBuffer,
8867 const SDL_GPUBufferLocation *source,
8868 const SDL_GPUBufferLocation *destination,
8869 Uint32 size,
8870 bool cycle)
8871{
8872 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
8873 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
8874 VulkanBufferContainer *srcContainer = (VulkanBufferContainer *)source->buffer;
8875 VulkanBufferContainer *dstContainer = (VulkanBufferContainer *)destination->buffer;
8876 VkBufferCopy bufferCopy;
8877
8878 VulkanBuffer *dstBuffer = VULKAN_INTERNAL_PrepareBufferForWrite(
8879 renderer,
8880 vulkanCommandBuffer,
8881 dstContainer,
8882 cycle,
8883 VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION);
8884
8885 VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
8886 renderer,
8887 vulkanCommandBuffer,
8888 VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE,
8889 srcContainer->activeBuffer);
8890
8891 bufferCopy.srcOffset = source->offset;
8892 bufferCopy.dstOffset = destination->offset;
8893 bufferCopy.size = size;
8894
8895 renderer->vkCmdCopyBuffer(
8896 vulkanCommandBuffer->commandBuffer,
8897 srcContainer->activeBuffer->buffer,
8898 dstBuffer->buffer,
8899 1,
8900 &bufferCopy);
8901
8902 VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
8903 renderer,
8904 vulkanCommandBuffer,
8905 VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE,
8906 srcContainer->activeBuffer);
8907
8908 VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
8909 renderer,
8910 vulkanCommandBuffer,
8911 VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION,
8912 dstBuffer);
8913
8914 VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, srcContainer->activeBuffer);
8915 VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, dstBuffer);
8916}
8917
8918static void VULKAN_GenerateMipmaps(
8919 SDL_GPUCommandBuffer *commandBuffer,
8920 SDL_GPUTexture *texture)
8921{
8922 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
8923 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
8924 VulkanTextureContainer *container = (VulkanTextureContainer *)texture;
8925 VulkanTextureSubresource *srcTextureSubresource;
8926 VulkanTextureSubresource *dstTextureSubresource;
8927 VkImageBlit blit;
8928
8929 // Blit each slice sequentially. Barriers, barriers everywhere!
8930 for (Uint32 layerOrDepthIndex = 0; layerOrDepthIndex < container->header.info.layer_count_or_depth; layerOrDepthIndex += 1)
8931 for (Uint32 level = 1; level < container->header.info.num_levels; level += 1) {
8932 Uint32 layer = container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : layerOrDepthIndex;
8933 Uint32 depth = container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? layerOrDepthIndex : 0;
8934
8935 Uint32 srcSubresourceIndex = VULKAN_INTERNAL_GetTextureSubresourceIndex(
8936 level - 1,
8937 layer,
8938 container->header.info.num_levels);
8939 Uint32 dstSubresourceIndex = VULKAN_INTERNAL_GetTextureSubresourceIndex(
8940 level,
8941 layer,
8942 container->header.info.num_levels);
8943
8944 srcTextureSubresource = &container->activeTexture->subresources[srcSubresourceIndex];
8945 dstTextureSubresource = &container->activeTexture->subresources[dstSubresourceIndex];
8946
8947 VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
8948 renderer,
8949 vulkanCommandBuffer,
8950 VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
8951 srcTextureSubresource);
8952
8953 VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
8954 renderer,
8955 vulkanCommandBuffer,
8956 VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
8957 dstTextureSubresource);
8958
8959 blit.srcOffsets[0].x = 0;
8960 blit.srcOffsets[0].y = 0;
8961 blit.srcOffsets[0].z = depth;
8962
8963 blit.srcOffsets[1].x = container->header.info.width >> (level - 1);
8964 blit.srcOffsets[1].y = container->header.info.height >> (level - 1);
8965 blit.srcOffsets[1].z = depth + 1;
8966
8967 blit.dstOffsets[0].x = 0;
8968 blit.dstOffsets[0].y = 0;
8969 blit.dstOffsets[0].z = depth;
8970
8971 blit.dstOffsets[1].x = container->header.info.width >> level;
8972 blit.dstOffsets[1].y = container->header.info.height >> level;
8973 blit.dstOffsets[1].z = depth + 1;
8974
8975 blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
8976 blit.srcSubresource.baseArrayLayer = layer;
8977 blit.srcSubresource.layerCount = 1;
8978 blit.srcSubresource.mipLevel = level - 1;
8979
8980 blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
8981 blit.dstSubresource.baseArrayLayer = layer;
8982 blit.dstSubresource.layerCount = 1;
8983 blit.dstSubresource.mipLevel = level;
8984
8985 renderer->vkCmdBlitImage(
8986 vulkanCommandBuffer->commandBuffer,
8987 container->activeTexture->image,
8988 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
8989 container->activeTexture->image,
8990 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
8991 1,
8992 &blit,
8993 VK_FILTER_LINEAR);
8994
8995 VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
8996 renderer,
8997 vulkanCommandBuffer,
8998 VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
8999 srcTextureSubresource);
9000
9001 VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
9002 renderer,
9003 vulkanCommandBuffer,
9004 VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
9005 dstTextureSubresource);
9006
9007 VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, srcTextureSubresource->parent);
9008 VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, dstTextureSubresource->parent);
9009 }
9010}
9011
9012static void VULKAN_EndCopyPass(
9013 SDL_GPUCommandBuffer *commandBuffer)
9014{
9015 // no-op
9016 (void)commandBuffer;
9017}
9018
9019static void VULKAN_Blit(
9020 SDL_GPUCommandBuffer *commandBuffer,
9021 const SDL_GPUBlitInfo *info)
9022{
9023 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
9024 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
9025 TextureCommonHeader *srcHeader = (TextureCommonHeader *)info->source.texture;
9026 TextureCommonHeader *dstHeader = (TextureCommonHeader *)info->destination.texture;
9027 VkImageBlit region;
9028 Uint32 srcLayer = srcHeader->info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : info->source.layer_or_depth_plane;
9029 Uint32 srcDepth = srcHeader->info.type == SDL_GPU_TEXTURETYPE_3D ? info->source.layer_or_depth_plane : 0;
9030 Uint32 dstLayer = dstHeader->info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : info->destination.layer_or_depth_plane;
9031 Uint32 dstDepth = dstHeader->info.type == SDL_GPU_TEXTURETYPE_3D ? info->destination.layer_or_depth_plane : 0;
9032 int32_t swap;
9033
9034 // Using BeginRenderPass to clear because vkCmdClearColorImage requires barriers anyway
9035 if (info->load_op == SDL_GPU_LOADOP_CLEAR) {
9036 SDL_GPUColorTargetInfo targetInfo;
9037 SDL_zero(targetInfo);
9038 targetInfo.texture = info->destination.texture;
9039 targetInfo.mip_level = info->destination.mip_level;
9040 targetInfo.layer_or_depth_plane = info->destination.layer_or_depth_plane;
9041 targetInfo.load_op = SDL_GPU_LOADOP_CLEAR;
9042 targetInfo.store_op = SDL_GPU_STOREOP_STORE;
9043 targetInfo.clear_color = info->clear_color;
9044 targetInfo.cycle = info->cycle;
9045 VULKAN_BeginRenderPass(
9046 commandBuffer,
9047 &targetInfo,
9048 1,
9049 NULL);
9050 VULKAN_EndRenderPass(commandBuffer);
9051 }
9052
9053 VulkanTextureSubresource *srcSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
9054 (VulkanTextureContainer *)info->source.texture,
9055 srcLayer,
9056 info->source.mip_level);
9057
9058 VulkanTextureSubresource *dstSubresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
9059 renderer,
9060 vulkanCommandBuffer,
9061 (VulkanTextureContainer *)info->destination.texture,
9062 dstLayer,
9063 info->destination.mip_level,
9064 info->cycle,
9065 VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION);
9066
9067 VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
9068 renderer,
9069 vulkanCommandBuffer,
9070 VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
9071 srcSubresource);
9072
9073 region.srcSubresource.aspectMask = srcSubresource->parent->aspectFlags;
9074 region.srcSubresource.baseArrayLayer = srcSubresource->layer;
9075 region.srcSubresource.layerCount = 1;
9076 region.srcSubresource.mipLevel = srcSubresource->level;
9077 region.srcOffsets[0].x = info->source.x;
9078 region.srcOffsets[0].y = info->source.y;
9079 region.srcOffsets[0].z = srcDepth;
9080 region.srcOffsets[1].x = info->source.x + info->source.w;
9081 region.srcOffsets[1].y = info->source.y + info->source.h;
9082 region.srcOffsets[1].z = srcDepth + 1;
9083
9084 if (info->flip_mode & SDL_FLIP_HORIZONTAL) {
9085 // flip the x positions
9086 swap = region.srcOffsets[0].x;
9087 region.srcOffsets[0].x = region.srcOffsets[1].x;
9088 region.srcOffsets[1].x = swap;
9089 }
9090
9091 if (info->flip_mode & SDL_FLIP_VERTICAL) {
9092 // flip the y positions
9093 swap = region.srcOffsets[0].y;
9094 region.srcOffsets[0].y = region.srcOffsets[1].y;
9095 region.srcOffsets[1].y = swap;
9096 }
9097
9098 region.dstSubresource.aspectMask = dstSubresource->parent->aspectFlags;
9099 region.dstSubresource.baseArrayLayer = dstSubresource->layer;
9100 region.dstSubresource.layerCount = 1;
9101 region.dstSubresource.mipLevel = dstSubresource->level;
9102 region.dstOffsets[0].x = info->destination.x;
9103 region.dstOffsets[0].y = info->destination.y;
9104 region.dstOffsets[0].z = dstDepth;
9105 region.dstOffsets[1].x = info->destination.x + info->destination.w;
9106 region.dstOffsets[1].y = info->destination.y + info->destination.h;
9107 region.dstOffsets[1].z = dstDepth + 1;
9108
9109 renderer->vkCmdBlitImage(
9110 vulkanCommandBuffer->commandBuffer,
9111 srcSubresource->parent->image,
9112 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
9113 dstSubresource->parent->image,
9114 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
9115 1,
9116 &region,
9117 SDLToVK_Filter[info->filter]);
9118
9119 VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
9120 renderer,
9121 vulkanCommandBuffer,
9122 VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
9123 srcSubresource);
9124
9125 VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
9126 renderer,
9127 vulkanCommandBuffer,
9128 VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
9129 dstSubresource);
9130
9131 VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, srcSubresource->parent);
9132 VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, dstSubresource->parent);
9133}
9134
9135static bool VULKAN_INTERNAL_AllocateCommandBuffer(
9136 VulkanRenderer *renderer,
9137 VulkanCommandPool *vulkanCommandPool)
9138{
9139 VkCommandBufferAllocateInfo allocateInfo;
9140 VkResult vulkanResult;
9141 VkCommandBuffer commandBufferHandle;
9142 VulkanCommandBuffer *commandBuffer;
9143
9144 vulkanCommandPool->inactiveCommandBufferCapacity += 1;
9145
9146 vulkanCommandPool->inactiveCommandBuffers = SDL_realloc(
9147 vulkanCommandPool->inactiveCommandBuffers,
9148 sizeof(VulkanCommandBuffer *) *
9149 vulkanCommandPool->inactiveCommandBufferCapacity);
9150
9151 allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
9152 allocateInfo.pNext = NULL;
9153 allocateInfo.commandPool = vulkanCommandPool->commandPool;
9154 allocateInfo.commandBufferCount = 1;
9155 allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
9156
9157 vulkanResult = renderer->vkAllocateCommandBuffers(
9158 renderer->logicalDevice,
9159 &allocateInfo,
9160 &commandBufferHandle);
9161
9162 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkAllocateCommandBuffers, false);
9163
9164 commandBuffer = SDL_malloc(sizeof(VulkanCommandBuffer));
9165 commandBuffer->renderer = renderer;
9166 commandBuffer->commandPool = vulkanCommandPool;
9167 commandBuffer->commandBuffer = commandBufferHandle;
9168
9169 commandBuffer->inFlightFence = VK_NULL_HANDLE;
9170
9171 // Presentation tracking
9172
9173 commandBuffer->presentDataCapacity = 1;
9174 commandBuffer->presentDataCount = 0;
9175 commandBuffer->presentDatas = SDL_malloc(
9176 commandBuffer->presentDataCapacity * sizeof(VulkanPresentData));
9177
9178 commandBuffer->waitSemaphoreCapacity = 1;
9179 commandBuffer->waitSemaphoreCount = 0;
9180 commandBuffer->waitSemaphores = SDL_malloc(
9181 commandBuffer->waitSemaphoreCapacity * sizeof(VkSemaphore));
9182
9183 commandBuffer->signalSemaphoreCapacity = 1;
9184 commandBuffer->signalSemaphoreCount = 0;
9185 commandBuffer->signalSemaphores = SDL_malloc(
9186 commandBuffer->signalSemaphoreCapacity * sizeof(VkSemaphore));
9187
9188 // Resource bind tracking
9189
9190 commandBuffer->needVertexBufferBind = false;
9191 commandBuffer->needNewVertexResourceDescriptorSet = true;
9192 commandBuffer->needNewVertexUniformDescriptorSet = true;
9193 commandBuffer->needNewVertexUniformOffsets = true;
9194 commandBuffer->needNewFragmentResourceDescriptorSet = true;
9195 commandBuffer->needNewFragmentUniformDescriptorSet = true;
9196 commandBuffer->needNewFragmentUniformOffsets = true;
9197
9198 commandBuffer->needNewComputeReadWriteDescriptorSet = true;
9199 commandBuffer->needNewComputeReadOnlyDescriptorSet = true;
9200 commandBuffer->needNewComputeUniformDescriptorSet = true;
9201 commandBuffer->needNewComputeUniformOffsets = true;
9202
9203 commandBuffer->vertexResourceDescriptorSet = VK_NULL_HANDLE;
9204 commandBuffer->vertexUniformDescriptorSet = VK_NULL_HANDLE;
9205 commandBuffer->fragmentResourceDescriptorSet = VK_NULL_HANDLE;
9206 commandBuffer->fragmentUniformDescriptorSet = VK_NULL_HANDLE;
9207
9208 commandBuffer->computeReadOnlyDescriptorSet = VK_NULL_HANDLE;
9209 commandBuffer->computeReadWriteDescriptorSet = VK_NULL_HANDLE;
9210 commandBuffer->computeUniformDescriptorSet = VK_NULL_HANDLE;
9211
9212 // Resource tracking
9213
9214 commandBuffer->usedBufferCapacity = 4;
9215 commandBuffer->usedBufferCount = 0;
9216 commandBuffer->usedBuffers = SDL_malloc(
9217 commandBuffer->usedBufferCapacity * sizeof(VulkanBuffer *));
9218
9219 commandBuffer->usedTextureCapacity = 4;
9220 commandBuffer->usedTextureCount = 0;
9221 commandBuffer->usedTextures = SDL_malloc(
9222 commandBuffer->usedTextureCapacity * sizeof(VulkanTexture *));
9223
9224 commandBuffer->usedSamplerCapacity = 4;
9225 commandBuffer->usedSamplerCount = 0;
9226 commandBuffer->usedSamplers = SDL_malloc(
9227 commandBuffer->usedSamplerCapacity * sizeof(VulkanSampler *));
9228
9229 commandBuffer->usedGraphicsPipelineCapacity = 4;
9230 commandBuffer->usedGraphicsPipelineCount = 0;
9231 commandBuffer->usedGraphicsPipelines = SDL_malloc(
9232 commandBuffer->usedGraphicsPipelineCapacity * sizeof(VulkanGraphicsPipeline *));
9233
9234 commandBuffer->usedComputePipelineCapacity = 4;
9235 commandBuffer->usedComputePipelineCount = 0;
9236 commandBuffer->usedComputePipelines = SDL_malloc(
9237 commandBuffer->usedComputePipelineCapacity * sizeof(VulkanComputePipeline *));
9238
9239 commandBuffer->usedFramebufferCapacity = 4;
9240 commandBuffer->usedFramebufferCount = 0;
9241 commandBuffer->usedFramebuffers = SDL_malloc(
9242 commandBuffer->usedFramebufferCapacity * sizeof(VulkanFramebuffer *));
9243
9244 commandBuffer->usedUniformBufferCapacity = 4;
9245 commandBuffer->usedUniformBufferCount = 0;
9246 commandBuffer->usedUniformBuffers = SDL_malloc(
9247 commandBuffer->usedUniformBufferCapacity * sizeof(VulkanUniformBuffer *));
9248
9249 // Pool it!
9250
9251 vulkanCommandPool->inactiveCommandBuffers[vulkanCommandPool->inactiveCommandBufferCount] = commandBuffer;
9252 vulkanCommandPool->inactiveCommandBufferCount += 1;
9253
9254 return true;
9255}
9256
9257static VulkanCommandPool *VULKAN_INTERNAL_FetchCommandPool(
9258 VulkanRenderer *renderer,
9259 SDL_ThreadID threadID)
9260{
9261 VulkanCommandPool *vulkanCommandPool = NULL;
9262 VkCommandPoolCreateInfo commandPoolCreateInfo;
9263 VkResult vulkanResult;
9264 CommandPoolHashTableKey key;
9265 key.threadID = threadID;
9266
9267 bool result = SDL_FindInHashTable(
9268 renderer->commandPoolHashTable,
9269 (const void *)&key,
9270 (const void **)&vulkanCommandPool);
9271
9272 if (result) {
9273 return vulkanCommandPool;
9274 }
9275
9276 vulkanCommandPool = (VulkanCommandPool *)SDL_malloc(sizeof(VulkanCommandPool));
9277
9278 commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
9279 commandPoolCreateInfo.pNext = NULL;
9280 commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
9281 commandPoolCreateInfo.queueFamilyIndex = renderer->queueFamilyIndex;
9282
9283 vulkanResult = renderer->vkCreateCommandPool(
9284 renderer->logicalDevice,
9285 &commandPoolCreateInfo,
9286 NULL,
9287 &vulkanCommandPool->commandPool);
9288
9289 if (vulkanResult != VK_SUCCESS) {
9290 SDL_free(vulkanCommandPool);
9291 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateCommandPool, NULL);
9292 return NULL;
9293 }
9294
9295 vulkanCommandPool->threadID = threadID;
9296
9297 vulkanCommandPool->inactiveCommandBufferCapacity = 0;
9298 vulkanCommandPool->inactiveCommandBufferCount = 0;
9299 vulkanCommandPool->inactiveCommandBuffers = NULL;
9300
9301 if (!VULKAN_INTERNAL_AllocateCommandBuffer(
9302 renderer,
9303 vulkanCommandPool)) {
9304 VULKAN_INTERNAL_DestroyCommandPool(renderer, vulkanCommandPool);
9305 return NULL;
9306 }
9307
9308 CommandPoolHashTableKey *allocedKey = SDL_malloc(sizeof(CommandPoolHashTableKey));
9309 allocedKey->threadID = threadID;
9310
9311 SDL_InsertIntoHashTable(
9312 renderer->commandPoolHashTable,
9313 (const void *)allocedKey,
9314 (const void *)vulkanCommandPool, true);
9315
9316 return vulkanCommandPool;
9317}
9318
9319static VulkanCommandBuffer *VULKAN_INTERNAL_GetInactiveCommandBufferFromPool(
9320 VulkanRenderer *renderer,
9321 SDL_ThreadID threadID)
9322{
9323 VulkanCommandPool *commandPool =
9324 VULKAN_INTERNAL_FetchCommandPool(renderer, threadID);
9325 VulkanCommandBuffer *commandBuffer;
9326
9327 if (commandPool == NULL) {
9328 return NULL;
9329 }
9330
9331 if (commandPool->inactiveCommandBufferCount == 0) {
9332 if (!VULKAN_INTERNAL_AllocateCommandBuffer(
9333 renderer,
9334 commandPool)) {
9335 return NULL;
9336 }
9337 }
9338
9339 commandBuffer = commandPool->inactiveCommandBuffers[commandPool->inactiveCommandBufferCount - 1];
9340 commandPool->inactiveCommandBufferCount -= 1;
9341
9342 return commandBuffer;
9343}
9344
9345static SDL_GPUCommandBuffer *VULKAN_AcquireCommandBuffer(
9346 SDL_GPURenderer *driverData)
9347{
9348 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
9349 VkResult result;
9350 Uint32 i;
9351
9352 SDL_ThreadID threadID = SDL_GetCurrentThreadID();
9353
9354 SDL_LockMutex(renderer->acquireCommandBufferLock);
9355
9356 VulkanCommandBuffer *commandBuffer =
9357 VULKAN_INTERNAL_GetInactiveCommandBufferFromPool(renderer, threadID);
9358
9359 commandBuffer->descriptorSetCache = VULKAN_INTERNAL_AcquireDescriptorSetCache(renderer);
9360
9361 SDL_UnlockMutex(renderer->acquireCommandBufferLock);
9362
9363 if (commandBuffer == NULL) {
9364 return NULL;
9365 }
9366
9367 // Reset state
9368
9369 commandBuffer->currentComputePipeline = NULL;
9370 commandBuffer->currentGraphicsPipeline = NULL;
9371
9372 SDL_zeroa(commandBuffer->colorAttachmentSubresources);
9373 SDL_zeroa(commandBuffer->resolveAttachmentSubresources);
9374 commandBuffer->depthStencilAttachmentSubresource = NULL;
9375 commandBuffer->colorAttachmentSubresourceCount = 0;
9376 commandBuffer->resolveAttachmentSubresourceCount = 0;
9377
9378 for (i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
9379 commandBuffer->vertexUniformBuffers[i] = NULL;
9380 commandBuffer->fragmentUniformBuffers[i] = NULL;
9381 commandBuffer->computeUniformBuffers[i] = NULL;
9382 }
9383
9384 commandBuffer->needVertexBufferBind = false;
9385 commandBuffer->needNewVertexResourceDescriptorSet = true;
9386 commandBuffer->needNewVertexUniformDescriptorSet = true;
9387 commandBuffer->needNewVertexUniformOffsets = true;
9388 commandBuffer->needNewFragmentResourceDescriptorSet = true;
9389 commandBuffer->needNewFragmentUniformDescriptorSet = true;
9390 commandBuffer->needNewFragmentUniformOffsets = true;
9391
9392 commandBuffer->needNewComputeReadOnlyDescriptorSet = true;
9393 commandBuffer->needNewComputeUniformDescriptorSet = true;
9394 commandBuffer->needNewComputeUniformOffsets = true;
9395
9396 commandBuffer->vertexResourceDescriptorSet = VK_NULL_HANDLE;
9397 commandBuffer->vertexUniformDescriptorSet = VK_NULL_HANDLE;
9398 commandBuffer->fragmentResourceDescriptorSet = VK_NULL_HANDLE;
9399 commandBuffer->fragmentUniformDescriptorSet = VK_NULL_HANDLE;
9400
9401 commandBuffer->computeReadOnlyDescriptorSet = VK_NULL_HANDLE;
9402 commandBuffer->computeReadWriteDescriptorSet = VK_NULL_HANDLE;
9403 commandBuffer->computeUniformDescriptorSet = VK_NULL_HANDLE;
9404
9405 SDL_zeroa(commandBuffer->vertexBuffers);
9406 SDL_zeroa(commandBuffer->vertexBufferOffsets);
9407 commandBuffer->vertexBufferCount = 0;
9408
9409 SDL_zeroa(commandBuffer->vertexSamplerTextures);
9410 SDL_zeroa(commandBuffer->vertexSamplers);
9411 SDL_zeroa(commandBuffer->vertexStorageTextures);
9412 SDL_zeroa(commandBuffer->vertexStorageBuffers);
9413
9414 SDL_zeroa(commandBuffer->fragmentSamplerTextures);
9415 SDL_zeroa(commandBuffer->fragmentSamplers);
9416 SDL_zeroa(commandBuffer->fragmentStorageTextures);
9417 SDL_zeroa(commandBuffer->fragmentStorageBuffers);
9418
9419 SDL_zeroa(commandBuffer->readWriteComputeStorageTextureSubresources);
9420 commandBuffer->readWriteComputeStorageTextureSubresourceCount = 0;
9421 SDL_zeroa(commandBuffer->readWriteComputeStorageBuffers);
9422 SDL_zeroa(commandBuffer->computeSamplerTextures);
9423 SDL_zeroa(commandBuffer->computeSamplers);
9424 SDL_zeroa(commandBuffer->readOnlyComputeStorageTextures);
9425 SDL_zeroa(commandBuffer->readOnlyComputeStorageBuffers);
9426
9427 commandBuffer->autoReleaseFence = true;
9428
9429 commandBuffer->isDefrag = 0;
9430
9431 /* Reset the command buffer here to avoid resets being called
9432 * from a separate thread than where the command buffer was acquired
9433 */
9434 result = renderer->vkResetCommandBuffer(
9435 commandBuffer->commandBuffer,
9436 VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
9437
9438 CHECK_VULKAN_ERROR_AND_RETURN(result, vkResetCommandBuffer, NULL);
9439
9440 if (!VULKAN_INTERNAL_BeginCommandBuffer(renderer, commandBuffer)) {
9441 return NULL;
9442 }
9443
9444 return (SDL_GPUCommandBuffer *)commandBuffer;
9445}
9446
9447static bool VULKAN_QueryFence(
9448 SDL_GPURenderer *driverData,
9449 SDL_GPUFence *fence)
9450{
9451 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
9452 VkResult result;
9453
9454 result = renderer->vkGetFenceStatus(
9455 renderer->logicalDevice,
9456 ((VulkanFenceHandle *)fence)->fence);
9457
9458 if (result == VK_SUCCESS) {
9459 return true;
9460 } else if (result == VK_NOT_READY) {
9461 return false;
9462 } else {
9463 SET_ERROR_AND_RETURN("vkGetFenceStatus: %s", VkErrorMessages(result), false);
9464 }
9465}
9466
9467static void VULKAN_INTERNAL_ReturnFenceToPool(
9468 VulkanRenderer *renderer,
9469 VulkanFenceHandle *fenceHandle)
9470{
9471 SDL_LockMutex(renderer->fencePool.lock);
9472
9473 EXPAND_ARRAY_IF_NEEDED(
9474 renderer->fencePool.availableFences,
9475 VulkanFenceHandle *,
9476 renderer->fencePool.availableFenceCount + 1,
9477 renderer->fencePool.availableFenceCapacity,
9478 renderer->fencePool.availableFenceCapacity * 2);
9479
9480 renderer->fencePool.availableFences[renderer->fencePool.availableFenceCount] = fenceHandle;
9481 renderer->fencePool.availableFenceCount += 1;
9482
9483 SDL_UnlockMutex(renderer->fencePool.lock);
9484}
9485
9486static void VULKAN_ReleaseFence(
9487 SDL_GPURenderer *driverData,
9488 SDL_GPUFence *fence)
9489{
9490 VulkanFenceHandle *handle = (VulkanFenceHandle *)fence;
9491
9492 if (SDL_AtomicDecRef(&handle->referenceCount)) {
9493 VULKAN_INTERNAL_ReturnFenceToPool((VulkanRenderer *)driverData, handle);
9494 }
9495}
9496
9497static WindowData *VULKAN_INTERNAL_FetchWindowData(
9498 SDL_Window *window)
9499{
9500 SDL_PropertiesID properties = SDL_GetWindowProperties(window);
9501 return (WindowData *)SDL_GetPointerProperty(properties, WINDOW_PROPERTY_DATA, NULL);
9502}
9503
9504static bool VULKAN_INTERNAL_OnWindowResize(void *userdata, SDL_Event *e)
9505{
9506 SDL_Window *w = (SDL_Window *)userdata;
9507 WindowData *data;
9508 if (e->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED && e->window.windowID == SDL_GetWindowID(w)) {
9509 data = VULKAN_INTERNAL_FetchWindowData(w);
9510 data->needsSwapchainRecreate = true;
9511 data->swapchainCreateWidth = e->window.data1;
9512 data->swapchainCreateHeight = e->window.data2;
9513 }
9514
9515 return true;
9516}
9517
9518static bool VULKAN_SupportsSwapchainComposition(
9519 SDL_GPURenderer *driverData,
9520 SDL_Window *window,
9521 SDL_GPUSwapchainComposition swapchainComposition)
9522{
9523 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
9524 WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window);
9525 VkSurfaceKHR surface;
9526 SwapchainSupportDetails supportDetails;
9527 bool result = false;
9528
9529 if (windowData == NULL) {
9530 SET_STRING_ERROR_AND_RETURN("Must claim window before querying swapchain composition support!", false);
9531 }
9532
9533 surface = windowData->surface;
9534 if (!surface) {
9535 SET_STRING_ERROR_AND_RETURN("Window has no Vulkan surface", false);
9536 }
9537
9538 if (VULKAN_INTERNAL_QuerySwapchainSupport(
9539 renderer,
9540 renderer->physicalDevice,
9541 surface,
9542 &supportDetails)) {
9543
9544 result = VULKAN_INTERNAL_VerifySwapSurfaceFormat(
9545 SwapchainCompositionToFormat[swapchainComposition],
9546 SwapchainCompositionToColorSpace[swapchainComposition],
9547 supportDetails.formats,
9548 supportDetails.formatsLength);
9549
9550 if (!result) {
9551 // Let's try again with the fallback format...
9552 result = VULKAN_INTERNAL_VerifySwapSurfaceFormat(
9553 SwapchainCompositionToFallbackFormat[swapchainComposition],
9554 SwapchainCompositionToColorSpace[swapchainComposition],
9555 supportDetails.formats,
9556 supportDetails.formatsLength);
9557 }
9558
9559 SDL_free(supportDetails.formats);
9560 SDL_free(supportDetails.presentModes);
9561 }
9562
9563 return result;
9564}
9565
9566static bool VULKAN_SupportsPresentMode(
9567 SDL_GPURenderer *driverData,
9568 SDL_Window *window,
9569 SDL_GPUPresentMode presentMode)
9570{
9571 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
9572 WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window);
9573 VkSurfaceKHR surface;
9574 SwapchainSupportDetails supportDetails;
9575 bool result = false;
9576
9577 if (windowData == NULL) {
9578 SET_STRING_ERROR_AND_RETURN("Must claim window before querying present mode support!", false);
9579 }
9580
9581 surface = windowData->surface;
9582 if (!surface) {
9583 SET_STRING_ERROR_AND_RETURN("Window has no Vulkan surface", false);
9584 }
9585
9586 if (VULKAN_INTERNAL_QuerySwapchainSupport(
9587 renderer,
9588 renderer->physicalDevice,
9589 surface,
9590 &supportDetails)) {
9591
9592 result = VULKAN_INTERNAL_VerifySwapPresentMode(
9593 SDLToVK_PresentMode[presentMode],
9594 supportDetails.presentModes,
9595 supportDetails.presentModesLength);
9596
9597 SDL_free(supportDetails.formats);
9598 SDL_free(supportDetails.presentModes);
9599 }
9600
9601 return result;
9602}
9603
9604static bool VULKAN_ClaimWindow(
9605 SDL_GPURenderer *driverData,
9606 SDL_Window *window)
9607{
9608 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
9609 WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window);
9610
9611 if (windowData == NULL) {
9612 windowData = SDL_calloc(1, sizeof(WindowData));
9613 windowData->window = window;
9614 windowData->presentMode = SDL_GPU_PRESENTMODE_VSYNC;
9615 windowData->swapchainComposition = SDL_GPU_SWAPCHAINCOMPOSITION_SDR;
9616
9617 // On non-Apple platforms the swapchain capability currentExtent can be different from the window,
9618 // so we have to query the window size.
9619#ifndef SDL_PLATFORM_APPLE
9620 int w, h;
9621 SDL_SyncWindow(window);
9622 SDL_GetWindowSizeInPixels(window, &w, &h);
9623 windowData->swapchainCreateWidth = w;
9624 windowData->swapchainCreateHeight = h;
9625#endif
9626
9627 Uint32 createSwapchainResult = VULKAN_INTERNAL_CreateSwapchain(renderer, windowData);
9628 if (createSwapchainResult == 1) {
9629 SDL_SetPointerProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA, windowData);
9630
9631 SDL_LockMutex(renderer->windowLock);
9632 if (renderer->claimedWindowCount >= renderer->claimedWindowCapacity) {
9633 renderer->claimedWindowCapacity *= 2;
9634 renderer->claimedWindows = SDL_realloc(
9635 renderer->claimedWindows,
9636 renderer->claimedWindowCapacity * sizeof(WindowData *));
9637 }
9638
9639 renderer->claimedWindows[renderer->claimedWindowCount] = windowData;
9640 renderer->claimedWindowCount += 1;
9641 SDL_UnlockMutex(renderer->windowLock);
9642
9643 SDL_AddEventWatch(VULKAN_INTERNAL_OnWindowResize, window);
9644
9645 return true;
9646 } else if (createSwapchainResult == VULKAN_INTERNAL_TRY_AGAIN) {
9647 windowData->needsSwapchainRecreate = true;
9648 return true;
9649 } else {
9650 SDL_free(windowData);
9651 return false;
9652 }
9653 } else {
9654 SET_STRING_ERROR_AND_RETURN("Window already claimed!", false);
9655 }
9656}
9657
9658static void VULKAN_ReleaseWindow(
9659 SDL_GPURenderer *driverData,
9660 SDL_Window *window)
9661{
9662 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
9663 WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window);
9664 Uint32 i;
9665
9666 if (windowData == NULL) {
9667 return;
9668 }
9669
9670 VULKAN_Wait(driverData);
9671
9672 for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
9673 if (windowData->inFlightFences[i] != NULL) {
9674 VULKAN_ReleaseFence(
9675 driverData,
9676 windowData->inFlightFences[i]);
9677 }
9678 }
9679
9680 VULKAN_INTERNAL_DestroySwapchain(
9681 (VulkanRenderer *)driverData,
9682 windowData);
9683
9684
9685 SDL_LockMutex(renderer->windowLock);
9686 for (i = 0; i < renderer->claimedWindowCount; i += 1) {
9687 if (renderer->claimedWindows[i]->window == window) {
9688 renderer->claimedWindows[i] = renderer->claimedWindows[renderer->claimedWindowCount - 1];
9689 renderer->claimedWindowCount -= 1;
9690 break;
9691 }
9692 }
9693 SDL_UnlockMutex(renderer->windowLock);
9694
9695 SDL_free(windowData);
9696
9697 SDL_ClearProperty(SDL_GetWindowProperties(window), WINDOW_PROPERTY_DATA);
9698 SDL_RemoveEventWatch(VULKAN_INTERNAL_OnWindowResize, window);
9699}
9700
9701static Uint32 VULKAN_INTERNAL_RecreateSwapchain(
9702 VulkanRenderer *renderer,
9703 WindowData *windowData)
9704{
9705 Uint32 i;
9706
9707 if (!VULKAN_Wait((SDL_GPURenderer *)renderer)) {
9708 return false;
9709 }
9710
9711 for (i = 0; i < MAX_FRAMES_IN_FLIGHT; i += 1) {
9712 if (windowData->inFlightFences[i] != NULL) {
9713 VULKAN_ReleaseFence(
9714 (SDL_GPURenderer *)renderer,
9715 windowData->inFlightFences[i]);
9716 windowData->inFlightFences[i] = NULL;
9717 }
9718 }
9719
9720 VULKAN_INTERNAL_DestroySwapchain(renderer, windowData);
9721 return VULKAN_INTERNAL_CreateSwapchain(renderer, windowData);
9722}
9723
9724static bool VULKAN_WaitForSwapchain(
9725 SDL_GPURenderer *driverData,
9726 SDL_Window *window)
9727{
9728 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
9729 WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window);
9730
9731 if (windowData == NULL) {
9732 SET_STRING_ERROR_AND_RETURN("Cannot wait for a swapchain from an unclaimed window!", false);
9733 }
9734
9735 if (windowData->inFlightFences[windowData->frameCounter] != NULL) {
9736 if (!VULKAN_WaitForFences(
9737 driverData,
9738 true,
9739 &windowData->inFlightFences[windowData->frameCounter],
9740 1)) {
9741 return false;
9742 }
9743 }
9744
9745 return true;
9746}
9747
9748static bool VULKAN_INTERNAL_AcquireSwapchainTexture(
9749 bool block,
9750 SDL_GPUCommandBuffer *commandBuffer,
9751 SDL_Window *window,
9752 SDL_GPUTexture **swapchainTexture,
9753 Uint32 *swapchainTextureWidth,
9754 Uint32 *swapchainTextureHeight)
9755{
9756 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
9757 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
9758 Uint32 swapchainImageIndex;
9759 WindowData *windowData;
9760 VkResult acquireResult = VK_SUCCESS;
9761 VulkanTextureContainer *swapchainTextureContainer = NULL;
9762 VulkanPresentData *presentData;
9763
9764 *swapchainTexture = NULL;
9765 if (swapchainTextureWidth) {
9766 *swapchainTextureWidth = 0;
9767 }
9768 if (swapchainTextureHeight) {
9769 *swapchainTextureHeight = 0;
9770 }
9771
9772 windowData = VULKAN_INTERNAL_FetchWindowData(window);
9773 if (windowData == NULL) {
9774 SET_STRING_ERROR_AND_RETURN("Cannot acquire a swapchain texture from an unclaimed window!", false);
9775 }
9776
9777 // If window data marked as needing swapchain recreate, try to recreate
9778 if (windowData->needsSwapchainRecreate) {
9779 Uint32 recreateSwapchainResult = VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData);
9780 if (!recreateSwapchainResult) {
9781 return false;
9782 } else if (recreateSwapchainResult == VULKAN_INTERNAL_TRY_AGAIN) {
9783 // Edge case, texture is filled in with NULL but not an error
9784 if (windowData->inFlightFences[windowData->frameCounter] != NULL) {
9785 VULKAN_ReleaseFence(
9786 (SDL_GPURenderer *)renderer,
9787 windowData->inFlightFences[windowData->frameCounter]);
9788 windowData->inFlightFences[windowData->frameCounter] = NULL;
9789 }
9790 return true;
9791 }
9792 }
9793
9794 if (swapchainTextureWidth) {
9795 *swapchainTextureWidth = windowData->width;
9796 }
9797 if (swapchainTextureHeight) {
9798 *swapchainTextureHeight = windowData->height;
9799 }
9800
9801 if (windowData->inFlightFences[windowData->frameCounter] != NULL) {
9802 if (block) {
9803 // If we are blocking, just wait for the fence!
9804 if (!VULKAN_WaitForFences(
9805 (SDL_GPURenderer *)renderer,
9806 true,
9807 &windowData->inFlightFences[windowData->frameCounter],
9808 1)) {
9809 return false;
9810 }
9811 } else {
9812 // If we are not blocking and the least recent fence is not signaled,
9813 // return true to indicate that there is no error but rendering should be skipped.
9814 if (!VULKAN_QueryFence(
9815 (SDL_GPURenderer *)renderer,
9816 windowData->inFlightFences[windowData->frameCounter])) {
9817 return true;
9818 }
9819 }
9820
9821 VULKAN_ReleaseFence(
9822 (SDL_GPURenderer *)renderer,
9823 windowData->inFlightFences[windowData->frameCounter]);
9824
9825 windowData->inFlightFences[windowData->frameCounter] = NULL;
9826 }
9827
9828 // Finally, try to acquire!
9829 while (true) {
9830 acquireResult = renderer->vkAcquireNextImageKHR(
9831 renderer->logicalDevice,
9832 windowData->swapchain,
9833 SDL_MAX_UINT64,
9834 windowData->imageAvailableSemaphore[windowData->frameCounter],
9835 VK_NULL_HANDLE,
9836 &swapchainImageIndex);
9837
9838 if (acquireResult == VK_SUCCESS || acquireResult == VK_SUBOPTIMAL_KHR) {
9839 break; // we got the next image!
9840 }
9841
9842 // If acquisition is invalid, let's try to recreate
9843 Uint32 recreateSwapchainResult = VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData);
9844 if (!recreateSwapchainResult) {
9845 return false;
9846 } else if (recreateSwapchainResult == VULKAN_INTERNAL_TRY_AGAIN) {
9847 // Edge case, texture is filled in with NULL but not an error
9848 return true;
9849 }
9850 }
9851
9852 swapchainTextureContainer = &windowData->textureContainers[swapchainImageIndex];
9853
9854 // We need a special execution dependency with pWaitDstStageMask or image transition can start before acquire finishes
9855
9856 VkImageMemoryBarrier imageBarrier;
9857 imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
9858 imageBarrier.pNext = NULL;
9859 imageBarrier.srcAccessMask = 0;
9860 imageBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
9861 imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
9862 imageBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
9863 imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
9864 imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
9865 imageBarrier.image = swapchainTextureContainer->activeTexture->image;
9866 imageBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
9867 imageBarrier.subresourceRange.baseMipLevel = 0;
9868 imageBarrier.subresourceRange.levelCount = 1;
9869 imageBarrier.subresourceRange.baseArrayLayer = 0;
9870 imageBarrier.subresourceRange.layerCount = 1;
9871
9872 renderer->vkCmdPipelineBarrier(
9873 vulkanCommandBuffer->commandBuffer,
9874 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
9875 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
9876 0,
9877 0,
9878 NULL,
9879 0,
9880 NULL,
9881 1,
9882 &imageBarrier);
9883
9884 // Set up present struct
9885
9886 if (vulkanCommandBuffer->presentDataCount == vulkanCommandBuffer->presentDataCapacity) {
9887 vulkanCommandBuffer->presentDataCapacity += 1;
9888 vulkanCommandBuffer->presentDatas = SDL_realloc(
9889 vulkanCommandBuffer->presentDatas,
9890 vulkanCommandBuffer->presentDataCapacity * sizeof(VulkanPresentData));
9891 }
9892
9893 presentData = &vulkanCommandBuffer->presentDatas[vulkanCommandBuffer->presentDataCount];
9894 vulkanCommandBuffer->presentDataCount += 1;
9895
9896 presentData->windowData = windowData;
9897 presentData->swapchainImageIndex = swapchainImageIndex;
9898
9899 // Set up present semaphores
9900
9901 if (vulkanCommandBuffer->waitSemaphoreCount == vulkanCommandBuffer->waitSemaphoreCapacity) {
9902 vulkanCommandBuffer->waitSemaphoreCapacity += 1;
9903 vulkanCommandBuffer->waitSemaphores = SDL_realloc(
9904 vulkanCommandBuffer->waitSemaphores,
9905 vulkanCommandBuffer->waitSemaphoreCapacity * sizeof(VkSemaphore));
9906 }
9907
9908 vulkanCommandBuffer->waitSemaphores[vulkanCommandBuffer->waitSemaphoreCount] =
9909 windowData->imageAvailableSemaphore[windowData->frameCounter];
9910 vulkanCommandBuffer->waitSemaphoreCount += 1;
9911
9912 if (vulkanCommandBuffer->signalSemaphoreCount == vulkanCommandBuffer->signalSemaphoreCapacity) {
9913 vulkanCommandBuffer->signalSemaphoreCapacity += 1;
9914 vulkanCommandBuffer->signalSemaphores = SDL_realloc(
9915 vulkanCommandBuffer->signalSemaphores,
9916 vulkanCommandBuffer->signalSemaphoreCapacity * sizeof(VkSemaphore));
9917 }
9918
9919 vulkanCommandBuffer->signalSemaphores[vulkanCommandBuffer->signalSemaphoreCount] =
9920 windowData->renderFinishedSemaphore[windowData->frameCounter];
9921 vulkanCommandBuffer->signalSemaphoreCount += 1;
9922
9923 *swapchainTexture = (SDL_GPUTexture *)swapchainTextureContainer;
9924 return true;
9925}
9926
9927static bool VULKAN_AcquireSwapchainTexture(
9928 SDL_GPUCommandBuffer *command_buffer,
9929 SDL_Window *window,
9930 SDL_GPUTexture **swapchain_texture,
9931 Uint32 *swapchain_texture_width,
9932 Uint32 *swapchain_texture_height
9933) {
9934 return VULKAN_INTERNAL_AcquireSwapchainTexture(
9935 false,
9936 command_buffer,
9937 window,
9938 swapchain_texture,
9939 swapchain_texture_width,
9940 swapchain_texture_height);
9941}
9942
9943static bool VULKAN_WaitAndAcquireSwapchainTexture(
9944 SDL_GPUCommandBuffer *command_buffer,
9945 SDL_Window *window,
9946 SDL_GPUTexture **swapchain_texture,
9947 Uint32 *swapchain_texture_width,
9948 Uint32 *swapchain_texture_height
9949) {
9950 return VULKAN_INTERNAL_AcquireSwapchainTexture(
9951 true,
9952 command_buffer,
9953 window,
9954 swapchain_texture,
9955 swapchain_texture_width,
9956 swapchain_texture_height);
9957}
9958
9959static SDL_GPUTextureFormat VULKAN_GetSwapchainTextureFormat(
9960 SDL_GPURenderer *driverData,
9961 SDL_Window *window)
9962{
9963 VulkanRenderer *renderer = (VulkanRenderer*)driverData;
9964 WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window);
9965
9966 if (windowData == NULL) {
9967 SET_STRING_ERROR_AND_RETURN("Cannot get swapchain format, window has not been claimed!", SDL_GPU_TEXTUREFORMAT_INVALID);
9968 }
9969
9970 return SwapchainCompositionToSDLFormat(
9971 windowData->swapchainComposition,
9972 windowData->usingFallbackFormat);
9973}
9974
9975static bool VULKAN_SetSwapchainParameters(
9976 SDL_GPURenderer *driverData,
9977 SDL_Window *window,
9978 SDL_GPUSwapchainComposition swapchainComposition,
9979 SDL_GPUPresentMode presentMode)
9980{
9981 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
9982 WindowData *windowData = VULKAN_INTERNAL_FetchWindowData(window);
9983
9984 if (windowData == NULL) {
9985 SET_STRING_ERROR_AND_RETURN("Cannot set swapchain parameters on unclaimed window!", false);
9986 }
9987
9988 if (!VULKAN_SupportsSwapchainComposition(driverData, window, swapchainComposition)) {
9989 SET_STRING_ERROR_AND_RETURN("Swapchain composition not supported!", false);
9990 }
9991
9992 if (!VULKAN_SupportsPresentMode(driverData, window, presentMode)) {
9993 SET_STRING_ERROR_AND_RETURN("Present mode not supported!", false);
9994 }
9995
9996 windowData->presentMode = presentMode;
9997 windowData->swapchainComposition = swapchainComposition;
9998
9999 Uint32 recreateSwapchainResult = VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData);
10000 if (!recreateSwapchainResult) {
10001 return false;
10002 } else if (recreateSwapchainResult == VULKAN_INTERNAL_TRY_AGAIN) {
10003 // Edge case, swapchain extent is (0, 0) but this is not an error
10004 windowData->needsSwapchainRecreate = true;
10005 return true;
10006 }
10007
10008 return true;
10009}
10010
10011static bool VULKAN_SetAllowedFramesInFlight(
10012 SDL_GPURenderer *driverData,
10013 Uint32 allowedFramesInFlight)
10014{
10015 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
10016
10017 renderer->allowedFramesInFlight = allowedFramesInFlight;
10018
10019 for (Uint32 i = 0; i < renderer->claimedWindowCount; i += 1) {
10020 WindowData *windowData = renderer->claimedWindows[i];
10021
10022 Uint32 recreateResult = VULKAN_INTERNAL_RecreateSwapchain(renderer, windowData);
10023 if (!recreateResult) {
10024 return false;
10025 } else if (recreateResult == VULKAN_INTERNAL_TRY_AGAIN) {
10026 // Edge case, swapchain extent is (0, 0) but this is not an error
10027 windowData->needsSwapchainRecreate = true;
10028 }
10029 }
10030
10031 return true;
10032}
10033
10034// Submission structure
10035
10036static VulkanFenceHandle *VULKAN_INTERNAL_AcquireFenceFromPool(
10037 VulkanRenderer *renderer)
10038{
10039 VulkanFenceHandle *handle;
10040 VkFenceCreateInfo fenceCreateInfo;
10041 VkFence fence;
10042 VkResult vulkanResult;
10043
10044 if (renderer->fencePool.availableFenceCount == 0) {
10045 // Create fence
10046 fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
10047 fenceCreateInfo.pNext = NULL;
10048 fenceCreateInfo.flags = 0;
10049
10050 vulkanResult = renderer->vkCreateFence(
10051 renderer->logicalDevice,
10052 &fenceCreateInfo,
10053 NULL,
10054 &fence);
10055
10056 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateFence, NULL);
10057
10058 handle = SDL_malloc(sizeof(VulkanFenceHandle));
10059 handle->fence = fence;
10060 SDL_SetAtomicInt(&handle->referenceCount, 0);
10061 return handle;
10062 }
10063
10064 SDL_LockMutex(renderer->fencePool.lock);
10065
10066 handle = renderer->fencePool.availableFences[renderer->fencePool.availableFenceCount - 1];
10067 renderer->fencePool.availableFenceCount -= 1;
10068
10069 vulkanResult = renderer->vkResetFences(
10070 renderer->logicalDevice,
10071 1,
10072 &handle->fence);
10073
10074 SDL_UnlockMutex(renderer->fencePool.lock);
10075
10076 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkResetFences, NULL);
10077
10078 return handle;
10079}
10080
10081static void VULKAN_INTERNAL_PerformPendingDestroys(
10082 VulkanRenderer *renderer)
10083{
10084 SDL_LockMutex(renderer->disposeLock);
10085
10086 for (Sint32 i = renderer->texturesToDestroyCount - 1; i >= 0; i -= 1) {
10087 if (SDL_GetAtomicInt(&renderer->texturesToDestroy[i]->referenceCount) == 0) {
10088 VULKAN_INTERNAL_DestroyTexture(
10089 renderer,
10090 renderer->texturesToDestroy[i]);
10091
10092 renderer->texturesToDestroy[i] = renderer->texturesToDestroy[renderer->texturesToDestroyCount - 1];
10093 renderer->texturesToDestroyCount -= 1;
10094 }
10095 }
10096
10097 for (Sint32 i = renderer->buffersToDestroyCount - 1; i >= 0; i -= 1) {
10098 if (SDL_GetAtomicInt(&renderer->buffersToDestroy[i]->referenceCount) == 0) {
10099 VULKAN_INTERNAL_DestroyBuffer(
10100 renderer,
10101 renderer->buffersToDestroy[i]);
10102
10103 renderer->buffersToDestroy[i] = renderer->buffersToDestroy[renderer->buffersToDestroyCount - 1];
10104 renderer->buffersToDestroyCount -= 1;
10105 }
10106 }
10107
10108 for (Sint32 i = renderer->graphicsPipelinesToDestroyCount - 1; i >= 0; i -= 1) {
10109 if (SDL_GetAtomicInt(&renderer->graphicsPipelinesToDestroy[i]->referenceCount) == 0) {
10110 VULKAN_INTERNAL_DestroyGraphicsPipeline(
10111 renderer,
10112 renderer->graphicsPipelinesToDestroy[i]);
10113
10114 renderer->graphicsPipelinesToDestroy[i] = renderer->graphicsPipelinesToDestroy[renderer->graphicsPipelinesToDestroyCount - 1];
10115 renderer->graphicsPipelinesToDestroyCount -= 1;
10116 }
10117 }
10118
10119 for (Sint32 i = renderer->computePipelinesToDestroyCount - 1; i >= 0; i -= 1) {
10120 if (SDL_GetAtomicInt(&renderer->computePipelinesToDestroy[i]->referenceCount) == 0) {
10121 VULKAN_INTERNAL_DestroyComputePipeline(
10122 renderer,
10123 renderer->computePipelinesToDestroy[i]);
10124
10125 renderer->computePipelinesToDestroy[i] = renderer->computePipelinesToDestroy[renderer->computePipelinesToDestroyCount - 1];
10126 renderer->computePipelinesToDestroyCount -= 1;
10127 }
10128 }
10129
10130 for (Sint32 i = renderer->shadersToDestroyCount - 1; i >= 0; i -= 1) {
10131 if (SDL_GetAtomicInt(&renderer->shadersToDestroy[i]->referenceCount) == 0) {
10132 VULKAN_INTERNAL_DestroyShader(
10133 renderer,
10134 renderer->shadersToDestroy[i]);
10135
10136 renderer->shadersToDestroy[i] = renderer->shadersToDestroy[renderer->shadersToDestroyCount - 1];
10137 renderer->shadersToDestroyCount -= 1;
10138 }
10139 }
10140
10141 for (Sint32 i = renderer->samplersToDestroyCount - 1; i >= 0; i -= 1) {
10142 if (SDL_GetAtomicInt(&renderer->samplersToDestroy[i]->referenceCount) == 0) {
10143 VULKAN_INTERNAL_DestroySampler(
10144 renderer,
10145 renderer->samplersToDestroy[i]);
10146
10147 renderer->samplersToDestroy[i] = renderer->samplersToDestroy[renderer->samplersToDestroyCount - 1];
10148 renderer->samplersToDestroyCount -= 1;
10149 }
10150 }
10151
10152 for (Sint32 i = renderer->framebuffersToDestroyCount - 1; i >= 0; i -= 1) {
10153 if (SDL_GetAtomicInt(&renderer->framebuffersToDestroy[i]->referenceCount) == 0) {
10154 VULKAN_INTERNAL_DestroyFramebuffer(
10155 renderer,
10156 renderer->framebuffersToDestroy[i]);
10157
10158 renderer->framebuffersToDestroy[i] = renderer->framebuffersToDestroy[renderer->framebuffersToDestroyCount - 1];
10159 renderer->framebuffersToDestroyCount -= 1;
10160 }
10161 }
10162
10163 SDL_UnlockMutex(renderer->disposeLock);
10164}
10165
10166static void VULKAN_INTERNAL_CleanCommandBuffer(
10167 VulkanRenderer *renderer,
10168 VulkanCommandBuffer *commandBuffer,
10169 bool cancel)
10170{
10171 if (commandBuffer->autoReleaseFence) {
10172 VULKAN_ReleaseFence(
10173 (SDL_GPURenderer *)renderer,
10174 (SDL_GPUFence *)commandBuffer->inFlightFence);
10175
10176 commandBuffer->inFlightFence = NULL;
10177 }
10178
10179 // Uniform buffers are now available
10180
10181 SDL_LockMutex(renderer->acquireUniformBufferLock);
10182
10183 for (Sint32 i = 0; i < commandBuffer->usedUniformBufferCount; i += 1) {
10184 VULKAN_INTERNAL_ReturnUniformBufferToPool(
10185 renderer,
10186 commandBuffer->usedUniformBuffers[i]);
10187 }
10188 commandBuffer->usedUniformBufferCount = 0;
10189
10190 SDL_UnlockMutex(renderer->acquireUniformBufferLock);
10191
10192 // Decrement reference counts
10193
10194 for (Sint32 i = 0; i < commandBuffer->usedBufferCount; i += 1) {
10195 (void)SDL_AtomicDecRef(&commandBuffer->usedBuffers[i]->referenceCount);
10196 }
10197 commandBuffer->usedBufferCount = 0;
10198
10199 for (Sint32 i = 0; i < commandBuffer->usedTextureCount; i += 1) {
10200 (void)SDL_AtomicDecRef(&commandBuffer->usedTextures[i]->referenceCount);
10201 }
10202 commandBuffer->usedTextureCount = 0;
10203
10204 for (Sint32 i = 0; i < commandBuffer->usedSamplerCount; i += 1) {
10205 (void)SDL_AtomicDecRef(&commandBuffer->usedSamplers[i]->referenceCount);
10206 }
10207 commandBuffer->usedSamplerCount = 0;
10208
10209 for (Sint32 i = 0; i < commandBuffer->usedGraphicsPipelineCount; i += 1) {
10210 (void)SDL_AtomicDecRef(&commandBuffer->usedGraphicsPipelines[i]->referenceCount);
10211 }
10212 commandBuffer->usedGraphicsPipelineCount = 0;
10213
10214 for (Sint32 i = 0; i < commandBuffer->usedComputePipelineCount; i += 1) {
10215 (void)SDL_AtomicDecRef(&commandBuffer->usedComputePipelines[i]->referenceCount);
10216 }
10217 commandBuffer->usedComputePipelineCount = 0;
10218
10219 for (Sint32 i = 0; i < commandBuffer->usedFramebufferCount; i += 1) {
10220 (void)SDL_AtomicDecRef(&commandBuffer->usedFramebuffers[i]->referenceCount);
10221 }
10222 commandBuffer->usedFramebufferCount = 0;
10223
10224 // Reset presentation data
10225
10226 commandBuffer->presentDataCount = 0;
10227 commandBuffer->waitSemaphoreCount = 0;
10228 commandBuffer->signalSemaphoreCount = 0;
10229
10230 // Reset defrag state
10231
10232 if (commandBuffer->isDefrag) {
10233 renderer->defragInProgress = 0;
10234 }
10235
10236 // Return command buffer to pool
10237
10238 SDL_LockMutex(renderer->acquireCommandBufferLock);
10239
10240 if (commandBuffer->commandPool->inactiveCommandBufferCount == commandBuffer->commandPool->inactiveCommandBufferCapacity) {
10241 commandBuffer->commandPool->inactiveCommandBufferCapacity += 1;
10242 commandBuffer->commandPool->inactiveCommandBuffers = SDL_realloc(
10243 commandBuffer->commandPool->inactiveCommandBuffers,
10244 commandBuffer->commandPool->inactiveCommandBufferCapacity * sizeof(VulkanCommandBuffer *));
10245 }
10246
10247 commandBuffer->commandPool->inactiveCommandBuffers[commandBuffer->commandPool->inactiveCommandBufferCount] = commandBuffer;
10248 commandBuffer->commandPool->inactiveCommandBufferCount += 1;
10249
10250 // Release descriptor set cache
10251
10252 VULKAN_INTERNAL_ReturnDescriptorSetCacheToPool(
10253 renderer,
10254 commandBuffer->descriptorSetCache);
10255
10256 commandBuffer->descriptorSetCache = NULL;
10257
10258 SDL_UnlockMutex(renderer->acquireCommandBufferLock);
10259
10260 // Remove this command buffer from the submitted list
10261 if (!cancel) {
10262 for (Uint32 i = 0; i < renderer->submittedCommandBufferCount; i += 1) {
10263 if (renderer->submittedCommandBuffers[i] == commandBuffer) {
10264 renderer->submittedCommandBuffers[i] = renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount - 1];
10265 renderer->submittedCommandBufferCount -= 1;
10266 }
10267 }
10268 }
10269}
10270
10271static bool VULKAN_WaitForFences(
10272 SDL_GPURenderer *driverData,
10273 bool waitAll,
10274 SDL_GPUFence *const *fences,
10275 Uint32 numFences)
10276{
10277 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
10278 VkFence *vkFences = SDL_stack_alloc(VkFence, numFences);
10279 VkResult result;
10280
10281 for (Uint32 i = 0; i < numFences; i += 1) {
10282 vkFences[i] = ((VulkanFenceHandle *)fences[i])->fence;
10283 }
10284
10285 result = renderer->vkWaitForFences(
10286 renderer->logicalDevice,
10287 numFences,
10288 vkFences,
10289 waitAll,
10290 SDL_MAX_UINT64);
10291
10292 CHECK_VULKAN_ERROR_AND_RETURN(result, vkWaitForFences, false);
10293
10294 SDL_stack_free(vkFences);
10295
10296 SDL_LockMutex(renderer->submitLock);
10297
10298 for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) {
10299 result = renderer->vkGetFenceStatus(
10300 renderer->logicalDevice,
10301 renderer->submittedCommandBuffers[i]->inFlightFence->fence);
10302
10303 if (result == VK_SUCCESS) {
10304 VULKAN_INTERNAL_CleanCommandBuffer(
10305 renderer,
10306 renderer->submittedCommandBuffers[i],
10307 false);
10308 }
10309 }
10310
10311 VULKAN_INTERNAL_PerformPendingDestroys(renderer);
10312
10313 SDL_UnlockMutex(renderer->submitLock);
10314
10315 return true;
10316}
10317
10318static bool VULKAN_Wait(
10319 SDL_GPURenderer *driverData)
10320{
10321 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
10322 VulkanCommandBuffer *commandBuffer;
10323 VkResult result;
10324 Sint32 i;
10325
10326 result = renderer->vkDeviceWaitIdle(renderer->logicalDevice);
10327
10328 CHECK_VULKAN_ERROR_AND_RETURN(result, vkDeviceWaitIdle, false);
10329
10330 SDL_LockMutex(renderer->submitLock);
10331
10332 for (i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) {
10333 commandBuffer = renderer->submittedCommandBuffers[i];
10334 VULKAN_INTERNAL_CleanCommandBuffer(renderer, commandBuffer, false);
10335 }
10336
10337 VULKAN_INTERNAL_PerformPendingDestroys(renderer);
10338
10339 SDL_UnlockMutex(renderer->submitLock);
10340
10341 return true;
10342}
10343
10344static SDL_GPUFence *VULKAN_SubmitAndAcquireFence(
10345 SDL_GPUCommandBuffer *commandBuffer)
10346{
10347 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
10348 vulkanCommandBuffer->autoReleaseFence = false;
10349 if (!VULKAN_Submit(commandBuffer)) {
10350 return NULL;
10351 }
10352 return (SDL_GPUFence *)vulkanCommandBuffer->inFlightFence;
10353}
10354
10355static void VULKAN_INTERNAL_ReleaseCommandBuffer(VulkanCommandBuffer *vulkanCommandBuffer)
10356{
10357 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
10358
10359 if (renderer->submittedCommandBufferCount + 1 >= renderer->submittedCommandBufferCapacity) {
10360 renderer->submittedCommandBufferCapacity = renderer->submittedCommandBufferCount + 1;
10361
10362 renderer->submittedCommandBuffers = SDL_realloc(
10363 renderer->submittedCommandBuffers,
10364 sizeof(VulkanCommandBuffer *) * renderer->submittedCommandBufferCapacity);
10365 }
10366
10367 renderer->submittedCommandBuffers[renderer->submittedCommandBufferCount] = vulkanCommandBuffer;
10368 renderer->submittedCommandBufferCount += 1;
10369}
10370
10371static bool VULKAN_Submit(
10372 SDL_GPUCommandBuffer *commandBuffer)
10373{
10374 VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
10375 VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
10376 VkSubmitInfo submitInfo;
10377 VkPresentInfoKHR presentInfo;
10378 VulkanPresentData *presentData;
10379 VkResult vulkanResult, presentResult = VK_SUCCESS;
10380 VkPipelineStageFlags waitStages[MAX_PRESENT_COUNT];
10381 Uint32 swapchainImageIndex;
10382 VulkanTextureSubresource *swapchainTextureSubresource;
10383 VulkanMemorySubAllocator *allocator;
10384 bool presenting = false;
10385
10386 SDL_LockMutex(renderer->submitLock);
10387
10388 // FIXME: Can this just be permanent?
10389 for (Uint32 i = 0; i < MAX_PRESENT_COUNT; i += 1) {
10390 waitStages[i] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
10391 }
10392
10393 for (Uint32 j = 0; j < vulkanCommandBuffer->presentDataCount; j += 1) {
10394 swapchainImageIndex = vulkanCommandBuffer->presentDatas[j].swapchainImageIndex;
10395 swapchainTextureSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
10396 &vulkanCommandBuffer->presentDatas[j].windowData->textureContainers[swapchainImageIndex],
10397 0,
10398 0);
10399
10400 VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
10401 renderer,
10402 vulkanCommandBuffer,
10403 VULKAN_TEXTURE_USAGE_MODE_PRESENT,
10404 swapchainTextureSubresource);
10405 }
10406
10407 if (!VULKAN_INTERNAL_EndCommandBuffer(renderer, vulkanCommandBuffer)) {
10408 SDL_UnlockMutex(renderer->submitLock);
10409 return false;
10410 }
10411
10412 vulkanCommandBuffer->inFlightFence = VULKAN_INTERNAL_AcquireFenceFromPool(renderer);
10413 if (vulkanCommandBuffer->inFlightFence == NULL) {
10414 SDL_UnlockMutex(renderer->submitLock);
10415 return false;
10416 }
10417
10418 // Command buffer has a reference to the in-flight fence
10419 (void)SDL_AtomicIncRef(&vulkanCommandBuffer->inFlightFence->referenceCount);
10420
10421 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
10422 submitInfo.pNext = NULL;
10423 submitInfo.commandBufferCount = 1;
10424 submitInfo.pCommandBuffers = &vulkanCommandBuffer->commandBuffer;
10425
10426 submitInfo.pWaitDstStageMask = waitStages;
10427 submitInfo.pWaitSemaphores = vulkanCommandBuffer->waitSemaphores;
10428 submitInfo.waitSemaphoreCount = vulkanCommandBuffer->waitSemaphoreCount;
10429 submitInfo.pSignalSemaphores = vulkanCommandBuffer->signalSemaphores;
10430 submitInfo.signalSemaphoreCount = vulkanCommandBuffer->signalSemaphoreCount;
10431
10432 vulkanResult = renderer->vkQueueSubmit(
10433 renderer->unifiedQueue,
10434 1,
10435 &submitInfo,
10436 vulkanCommandBuffer->inFlightFence->fence);
10437
10438 if (vulkanResult != VK_SUCCESS) {
10439 SDL_UnlockMutex(renderer->submitLock);
10440 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkQueueSubmit, false);
10441 }
10442
10443 // Present, if applicable
10444 bool result = true;
10445
10446 for (Uint32 j = 0; j < vulkanCommandBuffer->presentDataCount; j += 1) {
10447 presenting = true;
10448
10449 presentData = &vulkanCommandBuffer->presentDatas[j];
10450
10451 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
10452 presentInfo.pNext = NULL;
10453 presentInfo.pWaitSemaphores =
10454 &presentData->windowData->renderFinishedSemaphore[presentData->windowData->frameCounter];
10455 presentInfo.waitSemaphoreCount = 1;
10456 presentInfo.pSwapchains = &presentData->windowData->swapchain;
10457 presentInfo.swapchainCount = 1;
10458 presentInfo.pImageIndices = &presentData->swapchainImageIndex;
10459 presentInfo.pResults = NULL;
10460
10461 presentResult = renderer->vkQueuePresentKHR(
10462 renderer->unifiedQueue,
10463 &presentInfo);
10464
10465 if (presentResult == VK_SUCCESS || presentResult == VK_SUBOPTIMAL_KHR || presentResult == VK_ERROR_OUT_OF_DATE_KHR) {
10466 // If presenting, the swapchain is using the in-flight fence
10467 presentData->windowData->inFlightFences[presentData->windowData->frameCounter] = (SDL_GPUFence*)vulkanCommandBuffer->inFlightFence;
10468 (void)SDL_AtomicIncRef(&vulkanCommandBuffer->inFlightFence->referenceCount);
10469
10470 if (presentResult == VK_SUBOPTIMAL_KHR || presentResult == VK_ERROR_OUT_OF_DATE_KHR) {
10471 presentData->windowData->needsSwapchainRecreate = true;
10472 }
10473 } else {
10474 if (presentResult != VK_SUCCESS) {
10475 VULKAN_INTERNAL_ReleaseCommandBuffer(vulkanCommandBuffer);
10476 SDL_UnlockMutex(renderer->submitLock);
10477 }
10478
10479 CHECK_VULKAN_ERROR_AND_RETURN(presentResult, vkQueuePresentKHR, false);
10480 }
10481
10482 presentData->windowData->frameCounter =
10483 (presentData->windowData->frameCounter + 1) % renderer->allowedFramesInFlight;
10484 }
10485
10486 // Check if we can perform any cleanups
10487
10488 for (Sint32 i = renderer->submittedCommandBufferCount - 1; i >= 0; i -= 1) {
10489 vulkanResult = renderer->vkGetFenceStatus(
10490 renderer->logicalDevice,
10491 renderer->submittedCommandBuffers[i]->inFlightFence->fence);
10492
10493 if (vulkanResult == VK_SUCCESS) {
10494 VULKAN_INTERNAL_CleanCommandBuffer(
10495 renderer,
10496 renderer->submittedCommandBuffers[i],
10497 false);
10498 }
10499 }
10500
10501 if (renderer->checkEmptyAllocations) {
10502 SDL_LockMutex(renderer->allocatorLock);
10503
10504 for (Uint32 i = 0; i < VK_MAX_MEMORY_TYPES; i += 1) {
10505 allocator = &renderer->memoryAllocator->subAllocators[i];
10506
10507 for (Sint32 j = allocator->allocationCount - 1; j >= 0; j -= 1) {
10508 if (allocator->allocations[j]->usedRegionCount == 0) {
10509 VULKAN_INTERNAL_DeallocateMemory(
10510 renderer,
10511 allocator,
10512 j);
10513 }
10514 }
10515 }
10516
10517 renderer->checkEmptyAllocations = false;
10518
10519 SDL_UnlockMutex(renderer->allocatorLock);
10520 }
10521
10522 // Check pending destroys
10523 VULKAN_INTERNAL_PerformPendingDestroys(renderer);
10524
10525 // Defrag!
10526 if (
10527 presenting &&
10528 renderer->allocationsToDefragCount > 0 &&
10529 !renderer->defragInProgress) {
10530 result = VULKAN_INTERNAL_DefragmentMemory(renderer);
10531 }
10532
10533 // Mark command buffer as submitted
10534 // This must happen after defrag, because it will try to acquire new command buffers.
10535 VULKAN_INTERNAL_ReleaseCommandBuffer(vulkanCommandBuffer);
10536
10537 SDL_UnlockMutex(renderer->submitLock);
10538
10539 return result;
10540}
10541
10542static bool VULKAN_Cancel(
10543 SDL_GPUCommandBuffer *commandBuffer)
10544{
10545 VulkanRenderer *renderer;
10546 VulkanCommandBuffer *vulkanCommandBuffer;
10547 VkResult result;
10548
10549 vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
10550 renderer = vulkanCommandBuffer->renderer;
10551
10552 result = renderer->vkResetCommandBuffer(
10553 vulkanCommandBuffer->commandBuffer,
10554 VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
10555 CHECK_VULKAN_ERROR_AND_RETURN(result, vkResetCommandBuffer, false);
10556
10557 vulkanCommandBuffer->autoReleaseFence = false;
10558 SDL_LockMutex(renderer->submitLock);
10559 VULKAN_INTERNAL_CleanCommandBuffer(renderer, vulkanCommandBuffer, true);
10560 SDL_UnlockMutex(renderer->submitLock);
10561
10562 return true;
10563}
10564
10565static bool VULKAN_INTERNAL_DefragmentMemory(
10566 VulkanRenderer *renderer)
10567{
10568 VulkanMemoryAllocation *allocation;
10569 VulkanMemoryUsedRegion *currentRegion;
10570 VulkanBuffer *newBuffer;
10571 VulkanTexture *newTexture;
10572 VkBufferCopy bufferCopy;
10573 VkImageCopy imageCopy;
10574 VulkanCommandBuffer *commandBuffer;
10575 VulkanTextureSubresource *srcSubresource;
10576 VulkanTextureSubresource *dstSubresource;
10577 Uint32 i, subresourceIndex;
10578
10579 renderer->defragInProgress = 1;
10580
10581 commandBuffer = (VulkanCommandBuffer *)VULKAN_AcquireCommandBuffer((SDL_GPURenderer *)renderer);
10582 if (commandBuffer == NULL) {
10583 return false;
10584 }
10585 commandBuffer->isDefrag = 1;
10586
10587 SDL_LockMutex(renderer->allocatorLock);
10588
10589 allocation = renderer->allocationsToDefrag[renderer->allocationsToDefragCount - 1];
10590 renderer->allocationsToDefragCount -= 1;
10591
10592 /* For each used region in the allocation
10593 * create a new resource, copy the data
10594 * and re-point the resource containers
10595 */
10596 for (i = 0; i < allocation->usedRegionCount; i += 1) {
10597 currentRegion = allocation->usedRegions[i];
10598
10599 if (currentRegion->isBuffer && !currentRegion->vulkanBuffer->markedForDestroy) {
10600 currentRegion->vulkanBuffer->usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
10601
10602 newBuffer = VULKAN_INTERNAL_CreateBuffer(
10603 renderer,
10604 currentRegion->vulkanBuffer->size,
10605 currentRegion->vulkanBuffer->usage,
10606 currentRegion->vulkanBuffer->type,
10607 false,
10608 currentRegion->vulkanBuffer->container != NULL ? currentRegion->vulkanBuffer->container->debugName : NULL);
10609
10610 if (newBuffer == NULL) {
10611 SDL_UnlockMutex(renderer->allocatorLock);
10612 return false;
10613 }
10614
10615 // Copy buffer contents if necessary
10616 if (
10617 currentRegion->vulkanBuffer->type == VULKAN_BUFFER_TYPE_GPU && currentRegion->vulkanBuffer->transitioned) {
10618 VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
10619 renderer,
10620 commandBuffer,
10621 VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE,
10622 currentRegion->vulkanBuffer);
10623
10624 VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
10625 renderer,
10626 commandBuffer,
10627 VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION,
10628 newBuffer);
10629
10630 bufferCopy.srcOffset = 0;
10631 bufferCopy.dstOffset = 0;
10632 bufferCopy.size = currentRegion->resourceSize;
10633
10634 renderer->vkCmdCopyBuffer(
10635 commandBuffer->commandBuffer,
10636 currentRegion->vulkanBuffer->buffer,
10637 newBuffer->buffer,
10638 1,
10639 &bufferCopy);
10640
10641 VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
10642 renderer,
10643 commandBuffer,
10644 VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION,
10645 newBuffer);
10646
10647 VULKAN_INTERNAL_TrackBuffer(commandBuffer, currentRegion->vulkanBuffer);
10648 VULKAN_INTERNAL_TrackBuffer(commandBuffer, newBuffer);
10649 }
10650
10651 // re-point original container to new buffer
10652 newBuffer->container = currentRegion->vulkanBuffer->container;
10653 newBuffer->containerIndex = currentRegion->vulkanBuffer->containerIndex;
10654 if (newBuffer->type == VULKAN_BUFFER_TYPE_UNIFORM) {
10655 currentRegion->vulkanBuffer->uniformBufferForDefrag->buffer = newBuffer;
10656 } else {
10657 newBuffer->container->buffers[newBuffer->containerIndex] = newBuffer;
10658 if (newBuffer->container->activeBuffer == currentRegion->vulkanBuffer) {
10659 newBuffer->container->activeBuffer = newBuffer;
10660 }
10661 }
10662
10663 if (currentRegion->vulkanBuffer->uniformBufferForDefrag) {
10664 newBuffer->uniformBufferForDefrag = currentRegion->vulkanBuffer->uniformBufferForDefrag;
10665 }
10666
10667 VULKAN_INTERNAL_ReleaseBuffer(renderer, currentRegion->vulkanBuffer);
10668 } else if (!currentRegion->isBuffer && !currentRegion->vulkanTexture->markedForDestroy) {
10669 newTexture = VULKAN_INTERNAL_CreateTexture(
10670 renderer,
10671 &currentRegion->vulkanTexture->container->header.info);
10672
10673 if (newTexture == NULL) {
10674 SDL_UnlockMutex(renderer->allocatorLock);
10675 return false;
10676 }
10677
10678 SDL_GPUTextureCreateInfo info = currentRegion->vulkanTexture->container->header.info;
10679 for (subresourceIndex = 0; subresourceIndex < currentRegion->vulkanTexture->subresourceCount; subresourceIndex += 1) {
10680 // copy subresource if necessary
10681 srcSubresource = &currentRegion->vulkanTexture->subresources[subresourceIndex];
10682 dstSubresource = &newTexture->subresources[subresourceIndex];
10683
10684 VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
10685 renderer,
10686 commandBuffer,
10687 VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
10688 srcSubresource);
10689
10690 VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
10691 renderer,
10692 commandBuffer,
10693 VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
10694 dstSubresource);
10695
10696 imageCopy.srcOffset.x = 0;
10697 imageCopy.srcOffset.y = 0;
10698 imageCopy.srcOffset.z = 0;
10699 imageCopy.srcSubresource.aspectMask = srcSubresource->parent->aspectFlags;
10700 imageCopy.srcSubresource.baseArrayLayer = srcSubresource->layer;
10701 imageCopy.srcSubresource.layerCount = 1;
10702 imageCopy.srcSubresource.mipLevel = srcSubresource->level;
10703 imageCopy.extent.width = SDL_max(1, info.width >> srcSubresource->level);
10704 imageCopy.extent.height = SDL_max(1, info.height >> srcSubresource->level);
10705 imageCopy.extent.depth = info.type == SDL_GPU_TEXTURETYPE_3D ? info.layer_count_or_depth : 1;
10706 imageCopy.dstOffset.x = 0;
10707 imageCopy.dstOffset.y = 0;
10708 imageCopy.dstOffset.z = 0;
10709 imageCopy.dstSubresource.aspectMask = dstSubresource->parent->aspectFlags;
10710 imageCopy.dstSubresource.baseArrayLayer = dstSubresource->layer;
10711 imageCopy.dstSubresource.layerCount = 1;
10712 imageCopy.dstSubresource.mipLevel = dstSubresource->level;
10713
10714 renderer->vkCmdCopyImage(
10715 commandBuffer->commandBuffer,
10716 currentRegion->vulkanTexture->image,
10717 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
10718 newTexture->image,
10719 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
10720 1,
10721 &imageCopy);
10722
10723 VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
10724 renderer,
10725 commandBuffer,
10726 VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
10727 dstSubresource);
10728
10729 VULKAN_INTERNAL_TrackTexture(commandBuffer, srcSubresource->parent);
10730 VULKAN_INTERNAL_TrackTexture(commandBuffer, dstSubresource->parent);
10731 }
10732
10733 // re-point original container to new texture
10734 newTexture->container = currentRegion->vulkanTexture->container;
10735 newTexture->containerIndex = currentRegion->vulkanTexture->containerIndex;
10736 newTexture->container->textures[currentRegion->vulkanTexture->containerIndex] = newTexture;
10737 if (currentRegion->vulkanTexture == currentRegion->vulkanTexture->container->activeTexture) {
10738 newTexture->container->activeTexture = newTexture;
10739 }
10740
10741 VULKAN_INTERNAL_ReleaseTexture(renderer, currentRegion->vulkanTexture);
10742 }
10743 }
10744
10745 SDL_UnlockMutex(renderer->allocatorLock);
10746
10747 return VULKAN_Submit(
10748 (SDL_GPUCommandBuffer *)commandBuffer);
10749}
10750
10751// Format Info
10752
10753static bool VULKAN_SupportsTextureFormat(
10754 SDL_GPURenderer *driverData,
10755 SDL_GPUTextureFormat format,
10756 SDL_GPUTextureType type,
10757 SDL_GPUTextureUsageFlags usage)
10758{
10759 VulkanRenderer *renderer = (VulkanRenderer *)driverData;
10760 VkFormat vulkanFormat = SDLToVK_TextureFormat[format];
10761 VkImageUsageFlags vulkanUsage = 0;
10762 VkImageCreateFlags createFlags = 0;
10763 VkImageFormatProperties properties;
10764 VkResult vulkanResult;
10765
10766 if (usage & SDL_GPU_TEXTUREUSAGE_SAMPLER) {
10767 vulkanUsage |= VK_IMAGE_USAGE_SAMPLED_BIT;
10768 }
10769 if (usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
10770 vulkanUsage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
10771 }
10772 if (usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
10773 vulkanUsage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
10774 }
10775 if (usage & (SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ |
10776 SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ |
10777 SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE |
10778 SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE)) {
10779 vulkanUsage |= VK_IMAGE_USAGE_STORAGE_BIT;
10780 }
10781
10782 if (type == SDL_GPU_TEXTURETYPE_CUBE || type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) {
10783 createFlags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
10784 }
10785
10786 vulkanResult = renderer->vkGetPhysicalDeviceImageFormatProperties(
10787 renderer->physicalDevice,
10788 vulkanFormat,
10789 (type == SDL_GPU_TEXTURETYPE_3D) ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D,
10790 VK_IMAGE_TILING_OPTIMAL,
10791 vulkanUsage,
10792 createFlags,
10793 &properties);
10794
10795 return vulkanResult == VK_SUCCESS;
10796}
10797
10798// Device instantiation
10799
10800static inline Uint8 CheckDeviceExtensions(
10801 VkExtensionProperties *extensions,
10802 Uint32 numExtensions,
10803 VulkanExtensions *supports)
10804{
10805 Uint32 i;
10806
10807 SDL_memset(supports, '\0', sizeof(VulkanExtensions));
10808 for (i = 0; i < numExtensions; i += 1) {
10809 const char *name = extensions[i].extensionName;
10810#define CHECK(ext) \
10811 if (SDL_strcmp(name, "VK_" #ext) == 0) { \
10812 supports->ext = 1; \
10813 }
10814 CHECK(KHR_swapchain)
10815 else CHECK(KHR_maintenance1) else CHECK(KHR_driver_properties) else CHECK(KHR_portability_subset) else CHECK(EXT_texture_compression_astc_hdr)
10816#undef CHECK
10817 }
10818
10819 return (supports->KHR_swapchain &&
10820 supports->KHR_maintenance1);
10821}
10822
10823static inline Uint32 GetDeviceExtensionCount(VulkanExtensions *supports)
10824{
10825 return (
10826 supports->KHR_swapchain +
10827 supports->KHR_maintenance1 +
10828 supports->KHR_driver_properties +
10829 supports->KHR_portability_subset +
10830 supports->EXT_texture_compression_astc_hdr);
10831}
10832
10833static inline void CreateDeviceExtensionArray(
10834 VulkanExtensions *supports,
10835 const char **extensions)
10836{
10837 Uint8 cur = 0;
10838#define CHECK(ext) \
10839 if (supports->ext) { \
10840 extensions[cur++] = "VK_" #ext; \
10841 }
10842 CHECK(KHR_swapchain)
10843 CHECK(KHR_maintenance1)
10844 CHECK(KHR_driver_properties)
10845 CHECK(KHR_portability_subset)
10846 CHECK(EXT_texture_compression_astc_hdr)
10847#undef CHECK
10848}
10849
10850static inline Uint8 SupportsInstanceExtension(
10851 const char *ext,
10852 VkExtensionProperties *availableExtensions,
10853 Uint32 numAvailableExtensions)
10854{
10855 Uint32 i;
10856 for (i = 0; i < numAvailableExtensions; i += 1) {
10857 if (SDL_strcmp(ext, availableExtensions[i].extensionName) == 0) {
10858 return 1;
10859 }
10860 }
10861 return 0;
10862}
10863
10864static Uint8 VULKAN_INTERNAL_CheckInstanceExtensions(
10865 const char **requiredExtensions,
10866 Uint32 requiredExtensionsLength,
10867 bool *supportsDebugUtils,
10868 bool *supportsColorspace)
10869{
10870 Uint32 extensionCount, i;
10871 VkExtensionProperties *availableExtensions;
10872 Uint8 allExtensionsSupported = 1;
10873
10874 vkEnumerateInstanceExtensionProperties(
10875 NULL,
10876 &extensionCount,
10877 NULL);
10878 availableExtensions = SDL_malloc(
10879 extensionCount * sizeof(VkExtensionProperties));
10880 vkEnumerateInstanceExtensionProperties(
10881 NULL,
10882 &extensionCount,
10883 availableExtensions);
10884
10885 for (i = 0; i < requiredExtensionsLength; i += 1) {
10886 if (!SupportsInstanceExtension(
10887 requiredExtensions[i],
10888 availableExtensions,
10889 extensionCount)) {
10890 allExtensionsSupported = 0;
10891 break;
10892 }
10893 }
10894
10895 // This is optional, but nice to have!
10896 *supportsDebugUtils = SupportsInstanceExtension(
10897 VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
10898 availableExtensions,
10899 extensionCount);
10900
10901 // Also optional and nice to have!
10902 *supportsColorspace = SupportsInstanceExtension(
10903 VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME,
10904 availableExtensions,
10905 extensionCount);
10906
10907 SDL_free(availableExtensions);
10908 return allExtensionsSupported;
10909}
10910
10911static Uint8 VULKAN_INTERNAL_CheckDeviceExtensions(
10912 VulkanRenderer *renderer,
10913 VkPhysicalDevice physicalDevice,
10914 VulkanExtensions *physicalDeviceExtensions)
10915{
10916 Uint32 extensionCount;
10917 VkExtensionProperties *availableExtensions;
10918 Uint8 allExtensionsSupported;
10919
10920 renderer->vkEnumerateDeviceExtensionProperties(
10921 physicalDevice,
10922 NULL,
10923 &extensionCount,
10924 NULL);
10925 availableExtensions = (VkExtensionProperties *)SDL_malloc(
10926 extensionCount * sizeof(VkExtensionProperties));
10927 renderer->vkEnumerateDeviceExtensionProperties(
10928 physicalDevice,
10929 NULL,
10930 &extensionCount,
10931 availableExtensions);
10932
10933 allExtensionsSupported = CheckDeviceExtensions(
10934 availableExtensions,
10935 extensionCount,
10936 physicalDeviceExtensions);
10937
10938 SDL_free(availableExtensions);
10939 return allExtensionsSupported;
10940}
10941
10942static Uint8 VULKAN_INTERNAL_CheckValidationLayers(
10943 const char **validationLayers,
10944 Uint32 validationLayersLength)
10945{
10946 Uint32 layerCount;
10947 VkLayerProperties *availableLayers;
10948 Uint32 i, j;
10949 Uint8 layerFound = 0;
10950
10951 vkEnumerateInstanceLayerProperties(&layerCount, NULL);
10952 availableLayers = (VkLayerProperties *)SDL_malloc(
10953 layerCount * sizeof(VkLayerProperties));
10954 vkEnumerateInstanceLayerProperties(&layerCount, availableLayers);
10955
10956 for (i = 0; i < validationLayersLength; i += 1) {
10957 layerFound = 0;
10958
10959 for (j = 0; j < layerCount; j += 1) {
10960 if (SDL_strcmp(validationLayers[i], availableLayers[j].layerName) == 0) {
10961 layerFound = 1;
10962 break;
10963 }
10964 }
10965
10966 if (!layerFound) {
10967 break;
10968 }
10969 }
10970
10971 SDL_free(availableLayers);
10972 return layerFound;
10973}
10974
10975static Uint8 VULKAN_INTERNAL_CreateInstance(VulkanRenderer *renderer)
10976{
10977 VkResult vulkanResult;
10978 VkApplicationInfo appInfo;
10979 VkInstanceCreateFlags createFlags;
10980 const char *const *originalInstanceExtensionNames;
10981 const char **instanceExtensionNames;
10982 Uint32 instanceExtensionCount;
10983 VkInstanceCreateInfo createInfo;
10984 static const char *layerNames[] = { "VK_LAYER_KHRONOS_validation" };
10985
10986 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
10987 appInfo.pNext = NULL;
10988 appInfo.pApplicationName = NULL;
10989 appInfo.applicationVersion = 0;
10990 appInfo.pEngineName = "SDLGPU";
10991 appInfo.engineVersion = SDL_VERSION;
10992 appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0);
10993
10994 createFlags = 0;
10995
10996 originalInstanceExtensionNames = SDL_Vulkan_GetInstanceExtensions(&instanceExtensionCount);
10997 if (!originalInstanceExtensionNames) {
10998 SDL_LogError(
10999 SDL_LOG_CATEGORY_GPU,
11000 "SDL_Vulkan_GetInstanceExtensions(): getExtensionCount: %s",
11001 SDL_GetError());
11002
11003 return 0;
11004 }
11005
11006 /* Extra space for the following extensions:
11007 * VK_KHR_get_physical_device_properties2
11008 * VK_EXT_swapchain_colorspace
11009 * VK_EXT_debug_utils
11010 * VK_KHR_portability_enumeration
11011 */
11012 instanceExtensionNames = SDL_stack_alloc(
11013 const char *,
11014 instanceExtensionCount + 4);
11015 SDL_memcpy((void *)instanceExtensionNames, originalInstanceExtensionNames, instanceExtensionCount * sizeof(const char *));
11016
11017 // Core since 1.1
11018 instanceExtensionNames[instanceExtensionCount++] =
11019 VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;
11020
11021#ifdef SDL_PLATFORM_APPLE
11022 instanceExtensionNames[instanceExtensionCount++] =
11023 VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME;
11024 createFlags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
11025#endif
11026
11027 if (!VULKAN_INTERNAL_CheckInstanceExtensions(
11028 instanceExtensionNames,
11029 instanceExtensionCount,
11030 &renderer->supportsDebugUtils,
11031 &renderer->supportsColorspace)) {
11032 SDL_stack_free((char *)instanceExtensionNames);
11033 SET_STRING_ERROR_AND_RETURN("Required Vulkan instance extensions not supported", false);
11034 }
11035
11036 if (renderer->supportsDebugUtils) {
11037 // Append the debug extension
11038 instanceExtensionNames[instanceExtensionCount++] =
11039 VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
11040 } else {
11041 SDL_LogWarn(
11042 SDL_LOG_CATEGORY_GPU,
11043 "%s is not supported!",
11044 VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
11045 }
11046
11047 if (renderer->supportsColorspace) {
11048 // Append colorspace extension
11049 instanceExtensionNames[instanceExtensionCount++] =
11050 VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME;
11051 }
11052
11053 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
11054 createInfo.pNext = NULL;
11055 createInfo.flags = createFlags;
11056 createInfo.pApplicationInfo = &appInfo;
11057 createInfo.ppEnabledLayerNames = layerNames;
11058 createInfo.enabledExtensionCount = instanceExtensionCount;
11059 createInfo.ppEnabledExtensionNames = instanceExtensionNames;
11060 if (renderer->debugMode) {
11061 createInfo.enabledLayerCount = SDL_arraysize(layerNames);
11062 if (!VULKAN_INTERNAL_CheckValidationLayers(
11063 layerNames,
11064 createInfo.enabledLayerCount)) {
11065 SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Validation layers not found, continuing without validation");
11066 createInfo.enabledLayerCount = 0;
11067 } else {
11068 SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Validation layers enabled, expect debug level performance!");
11069 }
11070 } else {
11071 createInfo.enabledLayerCount = 0;
11072 }
11073
11074 vulkanResult = vkCreateInstance(&createInfo, NULL, &renderer->instance);
11075 SDL_stack_free((char *)instanceExtensionNames);
11076
11077 if (vulkanResult != VK_SUCCESS) {
11078 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateInstance, 0);
11079 }
11080
11081 return 1;
11082}
11083
11084static Uint8 VULKAN_INTERNAL_IsDeviceSuitable(
11085 VulkanRenderer *renderer,
11086 VkPhysicalDevice physicalDevice,
11087 VulkanExtensions *physicalDeviceExtensions,
11088 Uint32 *queueFamilyIndex,
11089 Uint8 *deviceRank)
11090{
11091 Uint32 queueFamilyCount, queueFamilyRank, queueFamilyBest;
11092 VkQueueFamilyProperties *queueProps;
11093 bool supportsPresent;
11094 VkPhysicalDeviceProperties deviceProperties;
11095 VkPhysicalDeviceFeatures deviceFeatures;
11096 Uint32 i;
11097
11098 const Uint8 *devicePriority = renderer->preferLowPower ? DEVICE_PRIORITY_LOWPOWER : DEVICE_PRIORITY_HIGHPERFORMANCE;
11099
11100 /* Get the device rank before doing any checks, in case one fails.
11101 * Note: If no dedicated device exists, one that supports our features
11102 * would be fine
11103 */
11104 renderer->vkGetPhysicalDeviceProperties(
11105 physicalDevice,
11106 &deviceProperties);
11107 if (*deviceRank < devicePriority[deviceProperties.deviceType]) {
11108 /* This device outranks the best device we've found so far!
11109 * This includes a dedicated GPU that has less features than an
11110 * integrated GPU, because this is a freak case that is almost
11111 * never intentionally desired by the end user
11112 */
11113 *deviceRank = devicePriority[deviceProperties.deviceType];
11114 } else if (*deviceRank > devicePriority[deviceProperties.deviceType]) {
11115 /* Device is outranked by a previous device, don't even try to
11116 * run a query and reset the rank to avoid overwrites
11117 */
11118 *deviceRank = 0;
11119 return 0;
11120 }
11121
11122 renderer->vkGetPhysicalDeviceFeatures(
11123 physicalDevice,
11124 &deviceFeatures);
11125 if (!deviceFeatures.independentBlend ||
11126 !deviceFeatures.imageCubeArray ||
11127 !deviceFeatures.depthClamp ||
11128 !deviceFeatures.shaderClipDistance ||
11129 !deviceFeatures.drawIndirectFirstInstance) {
11130 return 0;
11131 }
11132
11133 if (!VULKAN_INTERNAL_CheckDeviceExtensions(
11134 renderer,
11135 physicalDevice,
11136 physicalDeviceExtensions)) {
11137 return 0;
11138 }
11139
11140 renderer->vkGetPhysicalDeviceQueueFamilyProperties(
11141 physicalDevice,
11142 &queueFamilyCount,
11143 NULL);
11144
11145 queueProps = SDL_stack_alloc(
11146 VkQueueFamilyProperties,
11147 queueFamilyCount);
11148 renderer->vkGetPhysicalDeviceQueueFamilyProperties(
11149 physicalDevice,
11150 &queueFamilyCount,
11151 queueProps);
11152
11153 queueFamilyBest = 0;
11154 *queueFamilyIndex = SDL_MAX_UINT32;
11155 for (i = 0; i < queueFamilyCount; i += 1) {
11156 supportsPresent = SDL_Vulkan_GetPresentationSupport(
11157 renderer->instance,
11158 physicalDevice,
11159 i);
11160 if (!supportsPresent ||
11161 !(queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) {
11162 // Not a graphics family, ignore.
11163 continue;
11164 }
11165
11166 /* The queue family bitflags are kind of annoying.
11167 *
11168 * We of course need a graphics family, but we ideally want the
11169 * _primary_ graphics family. The spec states that at least one
11170 * graphics family must also be a compute family, so generally
11171 * drivers make that the first one. But hey, maybe something
11172 * genuinely can't do compute or something, and FNA doesn't
11173 * need it, so we'll be open to a non-compute queue family.
11174 *
11175 * Additionally, it's common to see the primary queue family
11176 * have the transfer bit set, which is great! But this is
11177 * actually optional; it's impossible to NOT have transfers in
11178 * graphics/compute but it _is_ possible for a graphics/compute
11179 * family, even the primary one, to just decide not to set the
11180 * bitflag. Admittedly, a driver may want to isolate transfer
11181 * queues to a dedicated family so that queues made solely for
11182 * transfers can have an optimized DMA queue.
11183 *
11184 * That, or the driver author got lazy and decided not to set
11185 * the bit. Looking at you, Android.
11186 *
11187 * -flibit
11188 */
11189 if (queueProps[i].queueFlags & VK_QUEUE_COMPUTE_BIT) {
11190 if (queueProps[i].queueFlags & VK_QUEUE_TRANSFER_BIT) {
11191 // Has all attribs!
11192 queueFamilyRank = 3;
11193 } else {
11194 // Probably has a DMA transfer queue family
11195 queueFamilyRank = 2;
11196 }
11197 } else {
11198 // Just a graphics family, probably has something better
11199 queueFamilyRank = 1;
11200 }
11201 if (queueFamilyRank > queueFamilyBest) {
11202 *queueFamilyIndex = i;
11203 queueFamilyBest = queueFamilyRank;
11204 }
11205 }
11206
11207 SDL_stack_free(queueProps);
11208
11209 if (*queueFamilyIndex == SDL_MAX_UINT32) {
11210 // Somehow no graphics queues existed. Compute-only device?
11211 return 0;
11212 }
11213
11214 // FIXME: Need better structure for checking vs storing swapchain support details
11215 return 1;
11216}
11217
11218static Uint8 VULKAN_INTERNAL_DeterminePhysicalDevice(VulkanRenderer *renderer)
11219{
11220 VkResult vulkanResult;
11221 VkPhysicalDevice *physicalDevices;
11222 VulkanExtensions *physicalDeviceExtensions;
11223 Uint32 i, physicalDeviceCount;
11224 Sint32 suitableIndex;
11225 Uint32 queueFamilyIndex, suitableQueueFamilyIndex;
11226 Uint8 deviceRank, highestRank;
11227
11228 vulkanResult = renderer->vkEnumeratePhysicalDevices(
11229 renderer->instance,
11230 &physicalDeviceCount,
11231 NULL);
11232 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkEnumeratePhysicalDevices, 0);
11233
11234 if (physicalDeviceCount == 0) {
11235 SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "Failed to find any GPUs with Vulkan support");
11236 return 0;
11237 }
11238
11239 physicalDevices = SDL_stack_alloc(VkPhysicalDevice, physicalDeviceCount);
11240 physicalDeviceExtensions = SDL_stack_alloc(VulkanExtensions, physicalDeviceCount);
11241
11242 vulkanResult = renderer->vkEnumeratePhysicalDevices(
11243 renderer->instance,
11244 &physicalDeviceCount,
11245 physicalDevices);
11246
11247 /* This should be impossible to hit, but from what I can tell this can
11248 * be triggered not because the array is too small, but because there
11249 * were drivers that turned out to be bogus, so this is the loader's way
11250 * of telling us that the list is now smaller than expected :shrug:
11251 */
11252 if (vulkanResult == VK_INCOMPLETE) {
11253 SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "vkEnumeratePhysicalDevices returned VK_INCOMPLETE, will keep trying anyway...");
11254 vulkanResult = VK_SUCCESS;
11255 }
11256
11257 if (vulkanResult != VK_SUCCESS) {
11258 SDL_LogWarn(
11259 SDL_LOG_CATEGORY_GPU,
11260 "vkEnumeratePhysicalDevices failed: %s",
11261 VkErrorMessages(vulkanResult));
11262 SDL_stack_free(physicalDevices);
11263 SDL_stack_free(physicalDeviceExtensions);
11264 return 0;
11265 }
11266
11267 // Any suitable device will do, but we'd like the best
11268 suitableIndex = -1;
11269 suitableQueueFamilyIndex = 0;
11270 highestRank = 0;
11271 for (i = 0; i < physicalDeviceCount; i += 1) {
11272 deviceRank = highestRank;
11273 if (VULKAN_INTERNAL_IsDeviceSuitable(
11274 renderer,
11275 physicalDevices[i],
11276 &physicalDeviceExtensions[i],
11277 &queueFamilyIndex,
11278 &deviceRank)) {
11279 /* Use this for rendering.
11280 * Note that this may override a previous device that
11281 * supports rendering, but shares the same device rank.
11282 */
11283 suitableIndex = i;
11284 suitableQueueFamilyIndex = queueFamilyIndex;
11285 highestRank = deviceRank;
11286 } else if (deviceRank > highestRank) {
11287 /* In this case, we found a... "realer?" GPU,
11288 * but it doesn't actually support our Vulkan.
11289 * We should disqualify all devices below as a
11290 * result, because if we don't we end up
11291 * ignoring real hardware and risk using
11292 * something like LLVMpipe instead!
11293 * -flibit
11294 */
11295 suitableIndex = -1;
11296 highestRank = deviceRank;
11297 }
11298 }
11299
11300 if (suitableIndex != -1) {
11301 renderer->supports = physicalDeviceExtensions[suitableIndex];
11302 renderer->physicalDevice = physicalDevices[suitableIndex];
11303 renderer->queueFamilyIndex = suitableQueueFamilyIndex;
11304 } else {
11305 SDL_stack_free(physicalDevices);
11306 SDL_stack_free(physicalDeviceExtensions);
11307 return 0;
11308 }
11309
11310 renderer->physicalDeviceProperties.sType =
11311 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
11312 if (renderer->supports.KHR_driver_properties) {
11313 renderer->physicalDeviceDriverProperties.sType =
11314 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR;
11315 renderer->physicalDeviceDriverProperties.pNext = NULL;
11316
11317 renderer->physicalDeviceProperties.pNext =
11318 &renderer->physicalDeviceDriverProperties;
11319
11320 renderer->vkGetPhysicalDeviceProperties2KHR(
11321 renderer->physicalDevice,
11322 &renderer->physicalDeviceProperties);
11323 } else {
11324 renderer->physicalDeviceProperties.pNext = NULL;
11325
11326 renderer->vkGetPhysicalDeviceProperties(
11327 renderer->physicalDevice,
11328 &renderer->physicalDeviceProperties.properties);
11329 }
11330
11331 renderer->vkGetPhysicalDeviceMemoryProperties(
11332 renderer->physicalDevice,
11333 &renderer->memoryProperties);
11334
11335 SDL_stack_free(physicalDevices);
11336 SDL_stack_free(physicalDeviceExtensions);
11337 return 1;
11338}
11339
11340static Uint8 VULKAN_INTERNAL_CreateLogicalDevice(
11341 VulkanRenderer *renderer)
11342{
11343 VkResult vulkanResult;
11344 VkDeviceCreateInfo deviceCreateInfo;
11345 VkPhysicalDeviceFeatures desiredDeviceFeatures;
11346 VkPhysicalDeviceFeatures haveDeviceFeatures;
11347 VkPhysicalDevicePortabilitySubsetFeaturesKHR portabilityFeatures;
11348 const char **deviceExtensions;
11349
11350 VkDeviceQueueCreateInfo queueCreateInfo;
11351 float queuePriority = 1.0f;
11352
11353 queueCreateInfo.sType =
11354 VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
11355 queueCreateInfo.pNext = NULL;
11356 queueCreateInfo.flags = 0;
11357 queueCreateInfo.queueFamilyIndex = renderer->queueFamilyIndex;
11358 queueCreateInfo.queueCount = 1;
11359 queueCreateInfo.pQueuePriorities = &queuePriority;
11360
11361 // check feature support
11362
11363 renderer->vkGetPhysicalDeviceFeatures(
11364 renderer->physicalDevice,
11365 &haveDeviceFeatures);
11366
11367 // specifying used device features
11368
11369 SDL_zero(desiredDeviceFeatures);
11370 desiredDeviceFeatures.independentBlend = VK_TRUE;
11371 desiredDeviceFeatures.samplerAnisotropy = VK_TRUE;
11372 desiredDeviceFeatures.imageCubeArray = VK_TRUE;
11373 desiredDeviceFeatures.depthClamp = VK_TRUE;
11374 desiredDeviceFeatures.shaderClipDistance = VK_TRUE;
11375 desiredDeviceFeatures.drawIndirectFirstInstance = VK_TRUE;
11376
11377 if (haveDeviceFeatures.fillModeNonSolid) {
11378 desiredDeviceFeatures.fillModeNonSolid = VK_TRUE;
11379 renderer->supportsFillModeNonSolid = true;
11380 }
11381
11382 if (haveDeviceFeatures.multiDrawIndirect) {
11383 desiredDeviceFeatures.multiDrawIndirect = VK_TRUE;
11384 renderer->supportsMultiDrawIndirect = true;
11385 }
11386
11387 // creating the logical device
11388
11389 deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
11390 if (renderer->supports.KHR_portability_subset) {
11391 portabilityFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR;
11392 portabilityFeatures.pNext = NULL;
11393 portabilityFeatures.constantAlphaColorBlendFactors = VK_FALSE;
11394 portabilityFeatures.events = VK_FALSE;
11395 portabilityFeatures.imageViewFormatReinterpretation = VK_FALSE;
11396 portabilityFeatures.imageViewFormatSwizzle = VK_TRUE;
11397 portabilityFeatures.imageView2DOn3DImage = VK_FALSE;
11398 portabilityFeatures.multisampleArrayImage = VK_FALSE;
11399 portabilityFeatures.mutableComparisonSamplers = VK_FALSE;
11400 portabilityFeatures.pointPolygons = VK_FALSE;
11401 portabilityFeatures.samplerMipLodBias = VK_FALSE; // Technically should be true, but eh
11402 portabilityFeatures.separateStencilMaskRef = VK_FALSE;
11403 portabilityFeatures.shaderSampleRateInterpolationFunctions = VK_FALSE;
11404 portabilityFeatures.tessellationIsolines = VK_FALSE;
11405 portabilityFeatures.tessellationPointMode = VK_FALSE;
11406 portabilityFeatures.triangleFans = VK_FALSE;
11407 portabilityFeatures.vertexAttributeAccessBeyondStride = VK_FALSE;
11408 deviceCreateInfo.pNext = &portabilityFeatures;
11409 } else {
11410 deviceCreateInfo.pNext = NULL;
11411 }
11412 deviceCreateInfo.flags = 0;
11413 deviceCreateInfo.queueCreateInfoCount = 1;
11414 deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
11415 deviceCreateInfo.enabledLayerCount = 0;
11416 deviceCreateInfo.ppEnabledLayerNames = NULL;
11417 deviceCreateInfo.enabledExtensionCount = GetDeviceExtensionCount(
11418 &renderer->supports);
11419 deviceExtensions = SDL_stack_alloc(
11420 const char *,
11421 deviceCreateInfo.enabledExtensionCount);
11422 CreateDeviceExtensionArray(&renderer->supports, deviceExtensions);
11423 deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions;
11424 deviceCreateInfo.pEnabledFeatures = &desiredDeviceFeatures;
11425
11426 vulkanResult = renderer->vkCreateDevice(
11427 renderer->physicalDevice,
11428 &deviceCreateInfo,
11429 NULL,
11430 &renderer->logicalDevice);
11431 SDL_stack_free((void *)deviceExtensions);
11432 CHECK_VULKAN_ERROR_AND_RETURN(vulkanResult, vkCreateDevice, 0);
11433
11434 // Load vkDevice entry points
11435
11436#define VULKAN_DEVICE_FUNCTION(func) \
11437 renderer->func = (PFN_##func) \
11438 renderer->vkGetDeviceProcAddr( \
11439 renderer->logicalDevice, \
11440 #func);
11441#include "SDL_gpu_vulkan_vkfuncs.h"
11442
11443 renderer->vkGetDeviceQueue(
11444 renderer->logicalDevice,
11445 renderer->queueFamilyIndex,
11446 0,
11447 &renderer->unifiedQueue);
11448
11449 return 1;
11450}
11451
11452static void VULKAN_INTERNAL_LoadEntryPoints(void)
11453{
11454 // Required for MoltenVK support
11455 SDL_setenv_unsafe("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
11456
11457 // Load Vulkan entry points
11458 if (!SDL_Vulkan_LoadLibrary(NULL)) {
11459 SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Vulkan: SDL_Vulkan_LoadLibrary failed!");
11460 return;
11461 }
11462
11463#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
11464#pragma GCC diagnostic push
11465#pragma GCC diagnostic ignored "-Wpedantic"
11466#endif
11467 vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr();
11468#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
11469#pragma GCC diagnostic pop
11470#endif
11471 if (vkGetInstanceProcAddr == NULL) {
11472 SDL_LogWarn(
11473 SDL_LOG_CATEGORY_GPU,
11474 "SDL_Vulkan_GetVkGetInstanceProcAddr(): %s",
11475 SDL_GetError());
11476 return;
11477 }
11478
11479#define VULKAN_GLOBAL_FUNCTION(name) \
11480 name = (PFN_##name)vkGetInstanceProcAddr(VK_NULL_HANDLE, #name); \
11481 if (name == NULL) { \
11482 SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "vkGetInstanceProcAddr(VK_NULL_HANDLE, \"" #name "\") failed"); \
11483 return; \
11484 }
11485#include "SDL_gpu_vulkan_vkfuncs.h"
11486}
11487
11488static bool VULKAN_INTERNAL_PrepareVulkan(
11489 VulkanRenderer *renderer)
11490{
11491 VULKAN_INTERNAL_LoadEntryPoints();
11492
11493 if (!VULKAN_INTERNAL_CreateInstance(renderer)) {
11494 SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Vulkan: Could not create Vulkan instance");
11495 return false;
11496 }
11497
11498#define VULKAN_INSTANCE_FUNCTION(func) \
11499 renderer->func = (PFN_##func)vkGetInstanceProcAddr(renderer->instance, #func);
11500#include "SDL_gpu_vulkan_vkfuncs.h"
11501
11502 if (!VULKAN_INTERNAL_DeterminePhysicalDevice(renderer)) {
11503 SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "Vulkan: Failed to determine a suitable physical device");
11504 return false;
11505 }
11506 return true;
11507}
11508
11509static bool VULKAN_PrepareDriver(SDL_VideoDevice *_this)
11510{
11511 // Set up dummy VulkanRenderer
11512 VulkanRenderer *renderer;
11513 Uint8 result;
11514
11515 if (_this->Vulkan_CreateSurface == NULL) {
11516 return false;
11517 }
11518
11519 if (!SDL_Vulkan_LoadLibrary(NULL)) {
11520 return false;
11521 }
11522
11523 renderer = (VulkanRenderer *)SDL_malloc(sizeof(VulkanRenderer));
11524 SDL_memset(renderer, '\0', sizeof(VulkanRenderer));
11525
11526 result = VULKAN_INTERNAL_PrepareVulkan(renderer);
11527
11528 if (result) {
11529 renderer->vkDestroyInstance(renderer->instance, NULL);
11530 }
11531 SDL_free(renderer);
11532 SDL_Vulkan_UnloadLibrary();
11533 return result;
11534}
11535
11536static SDL_GPUDevice *VULKAN_CreateDevice(bool debugMode, bool preferLowPower, SDL_PropertiesID props)
11537{
11538 VulkanRenderer *renderer;
11539
11540 SDL_GPUDevice *result;
11541 Uint32 i;
11542
11543 if (!SDL_Vulkan_LoadLibrary(NULL)) {
11544 SDL_assert(!"This should have failed in PrepareDevice first!");
11545 return NULL;
11546 }
11547
11548 renderer = (VulkanRenderer *)SDL_malloc(sizeof(VulkanRenderer));
11549 SDL_memset(renderer, '\0', sizeof(VulkanRenderer));
11550 renderer->debugMode = debugMode;
11551 renderer->preferLowPower = preferLowPower;
11552 renderer->allowedFramesInFlight = 2;
11553
11554 if (!VULKAN_INTERNAL_PrepareVulkan(renderer)) {
11555 SDL_free(renderer);
11556 SDL_Vulkan_UnloadLibrary();
11557 SET_STRING_ERROR_AND_RETURN("Failed to initialize Vulkan!", NULL);
11558 }
11559
11560 SDL_LogInfo(SDL_LOG_CATEGORY_GPU, "SDL_GPU Driver: Vulkan");
11561 SDL_LogInfo(
11562 SDL_LOG_CATEGORY_GPU,
11563 "Vulkan Device: %s",
11564 renderer->physicalDeviceProperties.properties.deviceName);
11565 if (renderer->supports.KHR_driver_properties) {
11566 SDL_LogInfo(
11567 SDL_LOG_CATEGORY_GPU,
11568 "Vulkan Driver: %s %s",
11569 renderer->physicalDeviceDriverProperties.driverName,
11570 renderer->physicalDeviceDriverProperties.driverInfo);
11571 SDL_LogInfo(
11572 SDL_LOG_CATEGORY_GPU,
11573 "Vulkan Conformance: %u.%u.%u",
11574 renderer->physicalDeviceDriverProperties.conformanceVersion.major,
11575 renderer->physicalDeviceDriverProperties.conformanceVersion.minor,
11576 renderer->physicalDeviceDriverProperties.conformanceVersion.patch);
11577 } else {
11578 SDL_LogWarn(SDL_LOG_CATEGORY_GPU, "KHR_driver_properties unsupported! Bother your vendor about this!");
11579 }
11580
11581 if (!VULKAN_INTERNAL_CreateLogicalDevice(
11582 renderer)) {
11583 SDL_free(renderer);
11584 SDL_Vulkan_UnloadLibrary();
11585 SET_STRING_ERROR_AND_RETURN("Failed to create logical device!", NULL);
11586 }
11587
11588 // FIXME: just move this into this function
11589 result = (SDL_GPUDevice *)SDL_malloc(sizeof(SDL_GPUDevice));
11590 ASSIGN_DRIVER(VULKAN)
11591
11592 result->driverData = (SDL_GPURenderer *)renderer;
11593
11594 /*
11595 * Create initial swapchain array
11596 */
11597
11598 renderer->claimedWindowCapacity = 1;
11599 renderer->claimedWindowCount = 0;
11600 renderer->claimedWindows = SDL_malloc(
11601 renderer->claimedWindowCapacity * sizeof(WindowData *));
11602
11603 // Threading
11604
11605 renderer->allocatorLock = SDL_CreateMutex();
11606 renderer->disposeLock = SDL_CreateMutex();
11607 renderer->submitLock = SDL_CreateMutex();
11608 renderer->acquireCommandBufferLock = SDL_CreateMutex();
11609 renderer->acquireUniformBufferLock = SDL_CreateMutex();
11610 renderer->framebufferFetchLock = SDL_CreateMutex();
11611 renderer->windowLock = SDL_CreateMutex();
11612
11613 /*
11614 * Create submitted command buffer list
11615 */
11616
11617 renderer->submittedCommandBufferCapacity = 16;
11618 renderer->submittedCommandBufferCount = 0;
11619 renderer->submittedCommandBuffers = SDL_malloc(sizeof(VulkanCommandBuffer *) * renderer->submittedCommandBufferCapacity);
11620
11621 // Memory Allocator
11622
11623 renderer->memoryAllocator = (VulkanMemoryAllocator *)SDL_malloc(
11624 sizeof(VulkanMemoryAllocator));
11625
11626 for (i = 0; i < VK_MAX_MEMORY_TYPES; i += 1) {
11627 renderer->memoryAllocator->subAllocators[i].memoryTypeIndex = i;
11628 renderer->memoryAllocator->subAllocators[i].allocations = NULL;
11629 renderer->memoryAllocator->subAllocators[i].allocationCount = 0;
11630 renderer->memoryAllocator->subAllocators[i].sortedFreeRegions = SDL_malloc(
11631 sizeof(VulkanMemoryFreeRegion *) * 4);
11632 renderer->memoryAllocator->subAllocators[i].sortedFreeRegionCount = 0;
11633 renderer->memoryAllocator->subAllocators[i].sortedFreeRegionCapacity = 4;
11634 }
11635
11636 // Create uniform buffer pool
11637
11638 renderer->uniformBufferPoolCount = 32;
11639 renderer->uniformBufferPoolCapacity = 32;
11640 renderer->uniformBufferPool = SDL_malloc(
11641 renderer->uniformBufferPoolCapacity * sizeof(VulkanUniformBuffer *));
11642
11643 for (i = 0; i < renderer->uniformBufferPoolCount; i += 1) {
11644 renderer->uniformBufferPool[i] = VULKAN_INTERNAL_CreateUniformBuffer(
11645 renderer,
11646 UNIFORM_BUFFER_SIZE);
11647 }
11648
11649 renderer->descriptorSetCachePoolCapacity = 8;
11650 renderer->descriptorSetCachePoolCount = 0;
11651 renderer->descriptorSetCachePool = SDL_calloc(renderer->descriptorSetCachePoolCapacity, sizeof(DescriptorSetCache *));
11652
11653 SDL_SetAtomicInt(&renderer->layoutResourceID, 0);
11654
11655 // Device limits
11656
11657 renderer->minUBOAlignment = (Uint32)renderer->physicalDeviceProperties.properties.limits.minUniformBufferOffsetAlignment;
11658
11659 // Initialize caches
11660
11661 renderer->commandPoolHashTable = SDL_CreateHashTable(
11662 0, // !!! FIXME: a real guess here, for a _minimum_ if not a maximum, could be useful.
11663 false, // manually synchronized due to submission timing
11664 VULKAN_INTERNAL_CommandPoolHashFunction,
11665 VULKAN_INTERNAL_CommandPoolHashKeyMatch,
11666 VULKAN_INTERNAL_CommandPoolHashDestroy,
11667 (void *)renderer);
11668
11669 renderer->renderPassHashTable = SDL_CreateHashTable(
11670 0, // !!! FIXME: a real guess here, for a _minimum_ if not a maximum, could be useful.
11671 true, // thread-safe
11672 VULKAN_INTERNAL_RenderPassHashFunction,
11673 VULKAN_INTERNAL_RenderPassHashKeyMatch,
11674 VULKAN_INTERNAL_RenderPassHashDestroy,
11675 (void *)renderer);
11676
11677 renderer->framebufferHashTable = SDL_CreateHashTable(
11678 0, // !!! FIXME: a real guess here, for a _minimum_ if not a maximum, could be useful.
11679 false, // manually synchronized due to iteration
11680 VULKAN_INTERNAL_FramebufferHashFunction,
11681 VULKAN_INTERNAL_FramebufferHashKeyMatch,
11682 VULKAN_INTERNAL_FramebufferHashDestroy,
11683 (void *)renderer);
11684
11685 renderer->graphicsPipelineResourceLayoutHashTable = SDL_CreateHashTable(
11686 0, // !!! FIXME: a real guess here, for a _minimum_ if not a maximum, could be useful.
11687 true, // thread-safe
11688 VULKAN_INTERNAL_GraphicsPipelineResourceLayoutHashFunction,
11689 VULKAN_INTERNAL_GraphicsPipelineResourceLayoutHashKeyMatch,
11690 VULKAN_INTERNAL_GraphicsPipelineResourceLayoutHashDestroy,
11691 (void *)renderer);
11692
11693 renderer->computePipelineResourceLayoutHashTable = SDL_CreateHashTable(
11694 0, // !!! FIXME: a real guess here, for a _minimum_ if not a maximum, could be useful.
11695 true, // thread-safe
11696 VULKAN_INTERNAL_ComputePipelineResourceLayoutHashFunction,
11697 VULKAN_INTERNAL_ComputePipelineResourceLayoutHashKeyMatch,
11698 VULKAN_INTERNAL_ComputePipelineResourceLayoutHashDestroy,
11699 (void *)renderer);
11700
11701 renderer->descriptorSetLayoutHashTable = SDL_CreateHashTable(
11702 0, // !!! FIXME: a real guess here, for a _minimum_ if not a maximum, could be useful.
11703 true, // thread-safe
11704 VULKAN_INTERNAL_DescriptorSetLayoutHashFunction,
11705 VULKAN_INTERNAL_DescriptorSetLayoutHashKeyMatch,
11706 VULKAN_INTERNAL_DescriptorSetLayoutHashDestroy,
11707 (void *)renderer);
11708
11709 // Initialize fence pool
11710
11711 renderer->fencePool.lock = SDL_CreateMutex();
11712
11713 renderer->fencePool.availableFenceCapacity = 4;
11714 renderer->fencePool.availableFenceCount = 0;
11715 renderer->fencePool.availableFences = SDL_malloc(
11716 renderer->fencePool.availableFenceCapacity * sizeof(VulkanFenceHandle *));
11717
11718 // Deferred destroy storage
11719
11720 renderer->texturesToDestroyCapacity = 16;
11721 renderer->texturesToDestroyCount = 0;
11722
11723 renderer->texturesToDestroy = (VulkanTexture **)SDL_malloc(
11724 sizeof(VulkanTexture *) *
11725 renderer->texturesToDestroyCapacity);
11726
11727 renderer->buffersToDestroyCapacity = 16;
11728 renderer->buffersToDestroyCount = 0;
11729
11730 renderer->buffersToDestroy = SDL_malloc(
11731 sizeof(VulkanBuffer *) *
11732 renderer->buffersToDestroyCapacity);
11733
11734 renderer->samplersToDestroyCapacity = 16;
11735 renderer->samplersToDestroyCount = 0;
11736
11737 renderer->samplersToDestroy = SDL_malloc(
11738 sizeof(VulkanSampler *) *
11739 renderer->samplersToDestroyCapacity);
11740
11741 renderer->graphicsPipelinesToDestroyCapacity = 16;
11742 renderer->graphicsPipelinesToDestroyCount = 0;
11743
11744 renderer->graphicsPipelinesToDestroy = SDL_malloc(
11745 sizeof(VulkanGraphicsPipeline *) *
11746 renderer->graphicsPipelinesToDestroyCapacity);
11747
11748 renderer->computePipelinesToDestroyCapacity = 16;
11749 renderer->computePipelinesToDestroyCount = 0;
11750
11751 renderer->computePipelinesToDestroy = SDL_malloc(
11752 sizeof(VulkanComputePipeline *) *
11753 renderer->computePipelinesToDestroyCapacity);
11754
11755 renderer->shadersToDestroyCapacity = 16;
11756 renderer->shadersToDestroyCount = 0;
11757
11758 renderer->shadersToDestroy = SDL_malloc(
11759 sizeof(VulkanShader *) *
11760 renderer->shadersToDestroyCapacity);
11761
11762 renderer->framebuffersToDestroyCapacity = 16;
11763 renderer->framebuffersToDestroyCount = 0;
11764 renderer->framebuffersToDestroy = SDL_malloc(
11765 sizeof(VulkanFramebuffer *) *
11766 renderer->framebuffersToDestroyCapacity);
11767
11768 // Defrag state
11769
11770 renderer->defragInProgress = 0;
11771
11772 renderer->allocationsToDefragCount = 0;
11773 renderer->allocationsToDefragCapacity = 4;
11774 renderer->allocationsToDefrag = SDL_malloc(
11775 renderer->allocationsToDefragCapacity * sizeof(VulkanMemoryAllocation *));
11776
11777 return result;
11778}
11779
11780SDL_GPUBootstrap VulkanDriver = {
11781 "vulkan",
11782 SDL_GPU_SHADERFORMAT_SPIRV,
11783 VULKAN_PrepareDriver,
11784 VULKAN_CreateDevice
11785};
11786
11787#endif // SDL_GPU_VULKAN
11788