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