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#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_RENDER_VULKAN
24
25#define SDL_VULKAN_FRAME_QUEUE_DEPTH 2
26#define SDL_VULKAN_NUM_VERTEX_BUFFERS 256
27#define SDL_VULKAN_VERTEX_BUFFER_DEFAULT_SIZE 65536
28#define SDL_VULKAN_CONSTANT_BUFFER_DEFAULT_SIZE 65536
29#define SDL_VULKAN_NUM_UPLOAD_BUFFERS 32
30#define SDL_VULKAN_MAX_DESCRIPTOR_SETS 4096
31
32#define SDL_VULKAN_VALIDATION_LAYER_NAME "VK_LAYER_KHRONOS_validation"
33
34#define VK_NO_PROTOTYPES
35#include "../../video/SDL_vulkan_internal.h"
36#include "../../video/SDL_sysvideo.h"
37#include "../SDL_sysrender.h"
38#include "../SDL_d3dmath.h"
39#include "../../video/SDL_pixels_c.h"
40#include "SDL_shaders_vulkan.h"
41
42#define SET_ERROR_CODE(message, rc) \
43 if (SDL_GetHintBoolean(SDL_HINT_RENDER_VULKAN_DEBUG, false)) { \
44 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "%s: %s", message, SDL_Vulkan_GetResultString(rc)); \
45 SDL_TriggerBreakpoint(); \
46 } \
47 SDL_SetError("%s: %s", message, SDL_Vulkan_GetResultString(rc)) \
48
49#define SET_ERROR_MESSAGE(message) \
50 if (SDL_GetHintBoolean(SDL_HINT_RENDER_VULKAN_DEBUG, false)) { \
51 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "%s", message); \
52 SDL_TriggerBreakpoint(); \
53 } \
54 SDL_SetError("%s", message) \
55
56#define VULKAN_FUNCTIONS() \
57 VULKAN_DEVICE_FUNCTION(vkAcquireNextImageKHR) \
58 VULKAN_DEVICE_FUNCTION(vkAllocateCommandBuffers) \
59 VULKAN_DEVICE_FUNCTION(vkAllocateDescriptorSets) \
60 VULKAN_DEVICE_FUNCTION(vkAllocateMemory) \
61 VULKAN_DEVICE_FUNCTION(vkBeginCommandBuffer) \
62 VULKAN_DEVICE_FUNCTION(vkBindBufferMemory) \
63 VULKAN_DEVICE_FUNCTION(vkBindImageMemory) \
64 VULKAN_DEVICE_FUNCTION(vkCmdBeginRenderPass) \
65 VULKAN_DEVICE_FUNCTION(vkCmdBindDescriptorSets) \
66 VULKAN_DEVICE_FUNCTION(vkCmdBindPipeline) \
67 VULKAN_DEVICE_FUNCTION(vkCmdBindVertexBuffers) \
68 VULKAN_DEVICE_FUNCTION(vkCmdClearColorImage) \
69 VULKAN_DEVICE_FUNCTION(vkCmdCopyBufferToImage) \
70 VULKAN_DEVICE_FUNCTION(vkCmdCopyImageToBuffer) \
71 VULKAN_DEVICE_FUNCTION(vkCmdDraw) \
72 VULKAN_DEVICE_FUNCTION(vkCmdEndRenderPass) \
73 VULKAN_DEVICE_FUNCTION(vkCmdPipelineBarrier) \
74 VULKAN_DEVICE_FUNCTION(vkCmdPushConstants) \
75 VULKAN_DEVICE_FUNCTION(vkCmdSetScissor) \
76 VULKAN_DEVICE_FUNCTION(vkCmdSetViewport) \
77 VULKAN_DEVICE_FUNCTION(vkCreateBuffer) \
78 VULKAN_DEVICE_FUNCTION(vkCreateCommandPool) \
79 VULKAN_DEVICE_FUNCTION(vkCreateDescriptorPool) \
80 VULKAN_DEVICE_FUNCTION(vkCreateDescriptorSetLayout) \
81 VULKAN_DEVICE_FUNCTION(vkCreateFence) \
82 VULKAN_DEVICE_FUNCTION(vkCreateFramebuffer) \
83 VULKAN_DEVICE_FUNCTION(vkCreateGraphicsPipelines) \
84 VULKAN_DEVICE_FUNCTION(vkCreateImage) \
85 VULKAN_DEVICE_FUNCTION(vkCreateImageView) \
86 VULKAN_DEVICE_FUNCTION(vkCreatePipelineLayout) \
87 VULKAN_DEVICE_FUNCTION(vkCreateRenderPass) \
88 VULKAN_DEVICE_FUNCTION(vkCreateSampler) \
89 VULKAN_DEVICE_FUNCTION(vkCreateSemaphore) \
90 VULKAN_DEVICE_FUNCTION(vkCreateShaderModule) \
91 VULKAN_DEVICE_FUNCTION(vkCreateSwapchainKHR) \
92 VULKAN_DEVICE_FUNCTION(vkDestroyBuffer) \
93 VULKAN_DEVICE_FUNCTION(vkDestroyCommandPool) \
94 VULKAN_DEVICE_FUNCTION(vkDestroyDevice) \
95 VULKAN_DEVICE_FUNCTION(vkDestroyDescriptorPool) \
96 VULKAN_DEVICE_FUNCTION(vkDestroyDescriptorSetLayout) \
97 VULKAN_DEVICE_FUNCTION(vkDestroyFence) \
98 VULKAN_DEVICE_FUNCTION(vkDestroyFramebuffer) \
99 VULKAN_DEVICE_FUNCTION(vkDestroyImage) \
100 VULKAN_DEVICE_FUNCTION(vkDestroyImageView) \
101 VULKAN_DEVICE_FUNCTION(vkDestroyPipeline) \
102 VULKAN_DEVICE_FUNCTION(vkDestroyPipelineLayout) \
103 VULKAN_DEVICE_FUNCTION(vkDestroyRenderPass) \
104 VULKAN_DEVICE_FUNCTION(vkDestroySampler) \
105 VULKAN_DEVICE_FUNCTION(vkDestroySemaphore) \
106 VULKAN_DEVICE_FUNCTION(vkDestroyShaderModule) \
107 VULKAN_DEVICE_FUNCTION(vkDestroySwapchainKHR) \
108 VULKAN_DEVICE_FUNCTION(vkDeviceWaitIdle) \
109 VULKAN_DEVICE_FUNCTION(vkEndCommandBuffer) \
110 VULKAN_DEVICE_FUNCTION(vkFreeCommandBuffers) \
111 VULKAN_DEVICE_FUNCTION(vkFreeMemory) \
112 VULKAN_DEVICE_FUNCTION(vkGetBufferMemoryRequirements) \
113 VULKAN_DEVICE_FUNCTION(vkGetImageMemoryRequirements) \
114 VULKAN_DEVICE_FUNCTION(vkGetDeviceQueue) \
115 VULKAN_DEVICE_FUNCTION(vkGetFenceStatus) \
116 VULKAN_DEVICE_FUNCTION(vkGetSwapchainImagesKHR) \
117 VULKAN_DEVICE_FUNCTION(vkMapMemory) \
118 VULKAN_DEVICE_FUNCTION(vkQueuePresentKHR) \
119 VULKAN_DEVICE_FUNCTION(vkQueueSubmit) \
120 VULKAN_DEVICE_FUNCTION(vkResetCommandBuffer) \
121 VULKAN_DEVICE_FUNCTION(vkResetCommandPool) \
122 VULKAN_DEVICE_FUNCTION(vkResetDescriptorPool) \
123 VULKAN_DEVICE_FUNCTION(vkResetFences) \
124 VULKAN_DEVICE_FUNCTION(vkUnmapMemory) \
125 VULKAN_DEVICE_FUNCTION(vkUpdateDescriptorSets) \
126 VULKAN_DEVICE_FUNCTION(vkWaitForFences) \
127 VULKAN_GLOBAL_FUNCTION(vkCreateInstance) \
128 VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceExtensionProperties) \
129 VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceLayerProperties) \
130 VULKAN_INSTANCE_FUNCTION(vkCreateDevice) \
131 VULKAN_INSTANCE_FUNCTION(vkDestroyInstance) \
132 VULKAN_INSTANCE_FUNCTION(vkDestroySurfaceKHR) \
133 VULKAN_INSTANCE_FUNCTION(vkEnumerateDeviceExtensionProperties) \
134 VULKAN_INSTANCE_FUNCTION(vkEnumeratePhysicalDevices) \
135 VULKAN_INSTANCE_FUNCTION(vkGetDeviceProcAddr) \
136 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures) \
137 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceProperties) \
138 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceMemoryProperties) \
139 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties) \
140 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \
141 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceFormatsKHR) \
142 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfacePresentModesKHR) \
143 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR) \
144 VULKAN_INSTANCE_FUNCTION(vkQueueWaitIdle) \
145 VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures2KHR) \
146 VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceFormatProperties2KHR) \
147 VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceImageFormatProperties2KHR) \
148 VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceMemoryProperties2KHR) \
149 VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceProperties2KHR) \
150 VULKAN_OPTIONAL_DEVICE_FUNCTION(vkCreateSamplerYcbcrConversionKHR) \
151 VULKAN_OPTIONAL_DEVICE_FUNCTION(vkDestroySamplerYcbcrConversionKHR) \
152
153#define VULKAN_DEVICE_FUNCTION(name) static PFN_##name name = NULL;
154#define VULKAN_GLOBAL_FUNCTION(name) static PFN_##name name = NULL;
155#define VULKAN_INSTANCE_FUNCTION(name) static PFN_##name name = NULL;
156#define VULKAN_OPTIONAL_INSTANCE_FUNCTION(name) static PFN_##name name = NULL;
157#define VULKAN_OPTIONAL_DEVICE_FUNCTION(name) static PFN_##name name = NULL;
158VULKAN_FUNCTIONS()
159#undef VULKAN_DEVICE_FUNCTION
160#undef VULKAN_GLOBAL_FUNCTION
161#undef VULKAN_INSTANCE_FUNCTION
162#undef VULKAN_OPTIONAL_INSTANCE_FUNCTION
163#undef VULKAN_OPTIONAL_DEVICE_FUNCTION
164
165// Renderpass types
166typedef enum {
167 VULKAN_RENDERPASS_LOAD = 0,
168 VULKAN_RENDERPASS_CLEAR = 1,
169 VULKAN_RENDERPASS_COUNT
170} VULKAN_RenderPass;
171
172// Sampler types
173typedef enum
174{
175 VULKAN_SAMPLER_NEAREST_CLAMP,
176 VULKAN_SAMPLER_NEAREST_WRAP,
177 VULKAN_SAMPLER_LINEAR_CLAMP,
178 VULKAN_SAMPLER_LINEAR_WRAP,
179 VULKAN_SAMPLER_COUNT
180} VULKAN_Sampler;
181
182// Vertex shader, common values
183typedef struct
184{
185 Float4X4 model;
186 Float4X4 projectionAndView;
187} VULKAN_VertexShaderConstants;
188
189// These should mirror the definitions in VULKAN_PixelShader_Common.hlsli
190//static const float TONEMAP_NONE = 0;
191//static const float TONEMAP_LINEAR = 1;
192static const float TONEMAP_CHROME = 2;
193
194static const float INPUTTYPE_UNSPECIFIED = 0;
195static const float INPUTTYPE_SRGB = 1;
196static const float INPUTTYPE_SCRGB = 2;
197static const float INPUTTYPE_HDR10 = 3;
198
199typedef enum
200{
201 SAMPLER_POINT_CLAMP,
202 SAMPLER_POINT_WRAP,
203 SAMPLER_LINEAR_CLAMP,
204 SAMPLER_LINEAR_WRAP,
205 NUM_SAMPLERS
206} Sampler;
207
208// Pixel shader constants, common values
209typedef struct
210{
211 float scRGB_output;
212 float input_type;
213 float color_scale;
214 float pixel_art;
215
216 float texel_width;
217 float texel_height;
218 float texture_width;
219 float texture_height;
220
221 float tonemap_method;
222 float tonemap_factor1;
223 float tonemap_factor2;
224 float sdr_white_point;
225} VULKAN_PixelShaderConstants;
226
227// Per-vertex data
228typedef struct
229{
230 float pos[2];
231 float tex[2];
232 SDL_FColor color;
233} VULKAN_VertexPositionColor;
234
235// Vulkan Buffer
236typedef struct
237{
238 VkDeviceMemory deviceMemory;
239 VkBuffer buffer;
240 VkDeviceSize size;
241 void *mappedBufferPtr;
242
243} VULKAN_Buffer;
244
245// Vulkan image
246typedef struct
247{
248 bool allocatedImage;
249 VkImage image;
250 VkImageView imageView;
251 VkDeviceMemory deviceMemory;
252 VkImageLayout imageLayout;
253 VkFormat format;
254} VULKAN_Image;
255
256// Per-texture data
257typedef struct
258{
259 VULKAN_Image mainImage;
260 VkRenderPass mainRenderpasses[VULKAN_RENDERPASS_COUNT];
261 VkFramebuffer mainFramebuffer;
262 VULKAN_Buffer stagingBuffer;
263 SDL_Rect lockedRect;
264 int width;
265 int height;
266 VULKAN_Shader shader;
267
268 // Object passed to VkImageView and VkSampler for doing Ycbcr -> RGB conversion
269 VkSamplerYcbcrConversion samplerYcbcrConversion;
270 // Sampler created with samplerYcbcrConversion, passed to PSO as immutable sampler
271 VkSampler samplerYcbcr;
272 // Descriptor set layout with samplerYcbcr baked as immutable sampler
273 VkDescriptorSetLayout descriptorSetLayoutYcbcr;
274 // Pipeline layout with immutable sampler descriptor set layout
275 VkPipelineLayout pipelineLayoutYcbcr;
276
277} VULKAN_TextureData;
278
279// Pipeline State Object data
280typedef struct
281{
282 VULKAN_Shader shader;
283 VULKAN_PixelShaderConstants shader_constants;
284 SDL_BlendMode blendMode;
285 VkPrimitiveTopology topology;
286 VkFormat format;
287 VkPipelineLayout pipelineLayout;
288 VkDescriptorSetLayout descriptorSetLayout;
289 VkPipeline pipeline;
290} VULKAN_PipelineState;
291
292typedef struct
293{
294 VkBuffer vertexBuffer;
295} VULKAN_DrawStateCache;
296
297// Private renderer data
298typedef struct
299{
300 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
301 VkInstance instance;
302 bool instance_external;
303 VkSurfaceKHR surface;
304 bool surface_external;
305 VkPhysicalDevice physicalDevice;
306 VkPhysicalDeviceProperties physicalDeviceProperties;
307 VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties;
308 VkPhysicalDeviceFeatures physicalDeviceFeatures;
309 VkQueue graphicsQueue;
310 VkQueue presentQueue;
311 VkDevice device;
312 bool device_external;
313 uint32_t graphicsQueueFamilyIndex;
314 uint32_t presentQueueFamilyIndex;
315 VkSwapchainKHR swapchain;
316 VkCommandPool commandPool;
317 VkCommandBuffer *commandBuffers;
318 uint32_t currentCommandBufferIndex;
319 VkCommandBuffer currentCommandBuffer;
320 VkFence *fences;
321 VkSurfaceCapabilitiesKHR surfaceCapabilities;
322 VkSurfaceFormatKHR *surfaceFormats;
323 bool recreateSwapchain;
324 int vsync;
325 SDL_PropertiesID create_props;
326
327 VkFramebuffer *framebuffers;
328 VkRenderPass renderPasses[VULKAN_RENDERPASS_COUNT];
329 VkRenderPass currentRenderPass;
330
331 VkShaderModule vertexShaderModules[NUM_SHADERS];
332 VkShaderModule fragmentShaderModules[NUM_SHADERS];
333 VkDescriptorSetLayout descriptorSetLayout;
334 VkPipelineLayout pipelineLayout;
335
336 // Vertex buffer data
337 VULKAN_Buffer vertexBuffers[SDL_VULKAN_NUM_VERTEX_BUFFERS];
338 VULKAN_VertexShaderConstants vertexShaderConstantsData;
339
340 // Data for staging/allocating textures
341 VULKAN_Buffer **uploadBuffers;
342 int *currentUploadBuffer;
343
344 // Data for updating constants
345 VULKAN_Buffer **constantBuffers;
346 uint32_t *numConstantBuffers;
347 uint32_t currentConstantBufferIndex;
348 int32_t currentConstantBufferOffset;
349
350 VkSampler samplers[VULKAN_SAMPLER_COUNT];
351 VkDescriptorPool **descriptorPools;
352 uint32_t *numDescriptorPools;
353 uint32_t currentDescriptorPoolIndex;
354 uint32_t currentDescriptorSetIndex;
355
356 int pipelineStateCount;
357 VULKAN_PipelineState *pipelineStates;
358 VULKAN_PipelineState *currentPipelineState;
359
360 bool supportsEXTSwapchainColorspace;
361 bool supportsKHRGetPhysicalDeviceProperties2;
362 bool supportsKHRSamplerYCbCrConversion;
363 uint32_t surfaceFormatsAllocatedCount;
364 uint32_t surfaceFormatsCount;
365 uint32_t swapchainDesiredImageCount;
366 VkSurfaceFormatKHR surfaceFormat;
367 VkExtent2D swapchainSize;
368 VkSurfaceTransformFlagBitsKHR swapChainPreTransform;
369 uint32_t swapchainImageCount;
370 VkImage *swapchainImages;
371 VkImageView *swapchainImageViews;
372 VkImageLayout *swapchainImageLayouts;
373 VkSemaphore *imageAvailableSemaphores;
374 VkSemaphore *renderingFinishedSemaphores;
375 VkSemaphore currentImageAvailableSemaphore;
376 uint32_t currentSwapchainImageIndex;
377
378 VkPipelineStageFlags *waitDestStageMasks;
379 VkSemaphore *waitRenderSemaphores;
380 uint32_t waitRenderSemaphoreCount;
381 uint32_t waitRenderSemaphoreMax;
382 VkSemaphore *signalRenderSemaphores;
383 uint32_t signalRenderSemaphoreCount;
384 uint32_t signalRenderSemaphoreMax;
385
386 // Cached renderer properties
387 VULKAN_TextureData *textureRenderTarget;
388 bool cliprectDirty;
389 bool currentCliprectEnabled;
390 SDL_Rect currentCliprect;
391 SDL_Rect currentViewport;
392 int currentViewportRotation;
393 bool viewportDirty;
394 Float4X4 identity;
395 VkComponentMapping identitySwizzle;
396 int currentVertexBuffer;
397 bool issueBatch;
398} VULKAN_RenderData;
399
400static SDL_PixelFormat VULKAN_VkFormatToSDLPixelFormat(VkFormat vkFormat)
401{
402 switch (vkFormat) {
403 case VK_FORMAT_B8G8R8A8_UNORM:
404 return SDL_PIXELFORMAT_ARGB8888;
405 case VK_FORMAT_R8G8B8A8_UNORM:
406 return SDL_PIXELFORMAT_ABGR8888;
407 case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
408 return SDL_PIXELFORMAT_ABGR2101010;
409 case VK_FORMAT_R16G16B16A16_SFLOAT:
410 return SDL_PIXELFORMAT_RGBA64_FLOAT;
411 default:
412 return SDL_PIXELFORMAT_UNKNOWN;
413 }
414}
415
416static int VULKAN_VkFormatGetNumPlanes(VkFormat vkFormat)
417{
418 switch (vkFormat) {
419 case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
420 return 3;
421 case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
422 case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
423 return 2;
424 default:
425 return 1;
426 }
427}
428
429static VkDeviceSize VULKAN_GetBytesPerPixel(VkFormat vkFormat)
430{
431 switch (vkFormat) {
432 case VK_FORMAT_R8_UNORM:
433 return 1;
434 case VK_FORMAT_R8G8_UNORM:
435 return 2;
436 case VK_FORMAT_R16G16_UNORM:
437 return 4;
438 case VK_FORMAT_B8G8R8A8_SRGB:
439 case VK_FORMAT_B8G8R8A8_UNORM:
440 case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
441 return 4;
442 case VK_FORMAT_R16G16B16A16_SFLOAT:
443 return 8;
444 default:
445 return 4;
446 }
447}
448
449static VkFormat SDLPixelFormatToVkTextureFormat(Uint32 format, Uint32 output_colorspace)
450{
451 switch (format) {
452 case SDL_PIXELFORMAT_RGBA64_FLOAT:
453 return VK_FORMAT_R16G16B16A16_SFLOAT;
454 case SDL_PIXELFORMAT_ABGR2101010:
455 return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
456 case SDL_PIXELFORMAT_ARGB8888:
457 if (output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
458 return VK_FORMAT_B8G8R8A8_SRGB;
459 }
460 return VK_FORMAT_B8G8R8A8_UNORM;
461 case SDL_PIXELFORMAT_ABGR8888:
462 if (output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
463 return VK_FORMAT_R8G8B8A8_SRGB;
464 }
465 return VK_FORMAT_R8G8B8A8_UNORM;
466 case SDL_PIXELFORMAT_YUY2:
467 return VK_FORMAT_G8B8G8R8_422_UNORM;
468 case SDL_PIXELFORMAT_UYVY:
469 return VK_FORMAT_B8G8R8G8_422_UNORM;
470 case SDL_PIXELFORMAT_YV12:
471 case SDL_PIXELFORMAT_IYUV:
472 return VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM;
473 case SDL_PIXELFORMAT_NV12:
474 case SDL_PIXELFORMAT_NV21:
475 return VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
476 case SDL_PIXELFORMAT_P010:
477 return VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16;
478 default:
479 return VK_FORMAT_UNDEFINED;
480 }
481}
482
483static void VULKAN_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture);
484static void VULKAN_DestroyBuffer(VULKAN_RenderData *rendererData, VULKAN_Buffer *vulkanBuffer);
485static void VULKAN_DestroyImage(VULKAN_RenderData *rendererData, VULKAN_Image *vulkanImage);
486static void VULKAN_ResetCommandList(VULKAN_RenderData *rendererData);
487static bool VULKAN_FindMemoryTypeIndex(VULKAN_RenderData *rendererData, uint32_t typeBits, VkMemoryPropertyFlags requiredFlags, VkMemoryPropertyFlags desiredFlags, uint32_t *memoryTypeIndexOut);
488static VkResult VULKAN_CreateWindowSizeDependentResources(SDL_Renderer *renderer);
489static VkDescriptorPool VULKAN_AllocateDescriptorPool(VULKAN_RenderData *rendererData);
490static VkResult VULKAN_CreateDescriptorSetAndPipelineLayout(VULKAN_RenderData *rendererData, VkSampler samplerYcbcr, VkDescriptorSetLayout *descriptorSetLayoutOut, VkPipelineLayout *pipelineLayoutOut);
491static VkSurfaceTransformFlagBitsKHR VULKAN_GetRotationForCurrentRenderTarget(VULKAN_RenderData *rendererData);
492static bool VULKAN_IsDisplayRotated90Degrees(VkSurfaceTransformFlagBitsKHR rotation);
493
494static void VULKAN_DestroyAll(SDL_Renderer *renderer)
495{
496 VULKAN_RenderData *rendererData;
497 if (renderer == NULL) {
498 return;
499 }
500 rendererData = (VULKAN_RenderData *)renderer->internal;
501 if (rendererData == NULL) {
502 return;
503 }
504
505 // Release all textures
506 for (SDL_Texture *texture = renderer->textures; texture; texture = texture->next) {
507 VULKAN_DestroyTexture(renderer, texture);
508 }
509
510 if (rendererData->waitDestStageMasks) {
511 SDL_free(rendererData->waitDestStageMasks);
512 rendererData->waitDestStageMasks = NULL;
513 }
514 if (rendererData->waitRenderSemaphores) {
515 SDL_free(rendererData->waitRenderSemaphores);
516 rendererData->waitRenderSemaphores = NULL;
517 }
518 if (rendererData->signalRenderSemaphores) {
519 SDL_free(rendererData->signalRenderSemaphores);
520 rendererData->signalRenderSemaphores = NULL;
521 }
522 if (rendererData->surfaceFormats != NULL) {
523 SDL_free(rendererData->surfaceFormats);
524 rendererData->surfaceFormats = NULL;
525 rendererData->surfaceFormatsAllocatedCount = 0;
526 }
527 if (rendererData->swapchainImages != NULL) {
528 SDL_free(rendererData->swapchainImages);
529 rendererData->swapchainImages = NULL;
530 }
531 if (rendererData->swapchain) {
532 vkDestroySwapchainKHR(rendererData->device, rendererData->swapchain, NULL);
533 rendererData->swapchain = VK_NULL_HANDLE;
534 }
535 if (rendererData->fences != NULL) {
536 for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
537 if (rendererData->fences[i] != VK_NULL_HANDLE) {
538 vkDestroyFence(rendererData->device, rendererData->fences[i], NULL);
539 rendererData->fences[i] = VK_NULL_HANDLE;
540 }
541 }
542 SDL_free(rendererData->fences);
543 rendererData->fences = NULL;
544 }
545 if (rendererData->swapchainImageViews) {
546 for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
547 if (rendererData->swapchainImageViews[i] != VK_NULL_HANDLE) {
548 vkDestroyImageView(rendererData->device, rendererData->swapchainImageViews[i], NULL);
549 }
550 }
551 SDL_free(rendererData->swapchainImageViews);
552 rendererData->swapchainImageViews = NULL;
553 }
554 if (rendererData->swapchainImageLayouts) {
555 SDL_free(rendererData->swapchainImageLayouts);
556 rendererData->swapchainImageLayouts = NULL;
557 }
558 if (rendererData->framebuffers) {
559 for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
560 if (rendererData->framebuffers[i] != VK_NULL_HANDLE) {
561 vkDestroyFramebuffer(rendererData->device, rendererData->framebuffers[i], NULL);
562 }
563 }
564 SDL_free(rendererData->framebuffers);
565 rendererData->framebuffers = NULL;
566 }
567 for (uint32_t i = 0; i < SDL_arraysize(rendererData->samplers); i++) {
568 if (rendererData->samplers[i] != VK_NULL_HANDLE) {
569 vkDestroySampler(rendererData->device, rendererData->samplers[i], NULL);
570 rendererData->samplers[i] = VK_NULL_HANDLE;
571 }
572 }
573 for (uint32_t i = 0; i < SDL_arraysize(rendererData->vertexBuffers); i++ ) {
574 VULKAN_DestroyBuffer(rendererData, &rendererData->vertexBuffers[i]);
575 }
576 SDL_memset(rendererData->vertexBuffers, 0, sizeof(rendererData->vertexBuffers));
577 for (uint32_t i = 0; i < VULKAN_RENDERPASS_COUNT; i++) {
578 if (rendererData->renderPasses[i] != VK_NULL_HANDLE) {
579 vkDestroyRenderPass(rendererData->device, rendererData->renderPasses[i], NULL);
580 rendererData->renderPasses[i] = VK_NULL_HANDLE;
581 }
582 }
583 if (rendererData->imageAvailableSemaphores) {
584 for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) {
585 if (rendererData->imageAvailableSemaphores[i] != VK_NULL_HANDLE) {
586 vkDestroySemaphore(rendererData->device, rendererData->imageAvailableSemaphores[i], NULL);
587 }
588 }
589 SDL_free(rendererData->imageAvailableSemaphores);
590 rendererData->imageAvailableSemaphores = NULL;
591 }
592 if (rendererData->renderingFinishedSemaphores) {
593 for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) {
594 if (rendererData->renderingFinishedSemaphores[i] != VK_NULL_HANDLE) {
595 vkDestroySemaphore(rendererData->device, rendererData->renderingFinishedSemaphores[i], NULL);
596 }
597 }
598 SDL_free(rendererData->renderingFinishedSemaphores);
599 rendererData->renderingFinishedSemaphores = NULL;
600 }
601 if (rendererData->commandBuffers) {
602 vkFreeCommandBuffers(rendererData->device, rendererData->commandPool, rendererData->swapchainImageCount, rendererData->commandBuffers);
603 SDL_free(rendererData->commandBuffers);
604 rendererData->commandBuffers = NULL;
605 rendererData->currentCommandBuffer = VK_NULL_HANDLE;
606 rendererData->currentCommandBufferIndex = 0;
607 }
608 if (rendererData->commandPool) {
609 vkDestroyCommandPool(rendererData->device, rendererData->commandPool, NULL);
610 rendererData->commandPool = VK_NULL_HANDLE;
611 }
612 if (rendererData->descriptorPools) {
613 SDL_assert(rendererData->numDescriptorPools);
614 for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
615 for (uint32_t j = 0; j < rendererData->numDescriptorPools[i]; j++) {
616 if (rendererData->descriptorPools[i][j] != VK_NULL_HANDLE) {
617 vkDestroyDescriptorPool(rendererData->device, rendererData->descriptorPools[i][j], NULL);
618 }
619 }
620 SDL_free(rendererData->descriptorPools[i]);
621 }
622 SDL_free(rendererData->descriptorPools);
623 rendererData->descriptorPools = NULL;
624 SDL_free(rendererData->numDescriptorPools);
625 rendererData->numDescriptorPools = NULL;
626 }
627 for (uint32_t i = 0; i < NUM_SHADERS; i++) {
628 if (rendererData->vertexShaderModules[i] != VK_NULL_HANDLE) {
629 vkDestroyShaderModule(rendererData->device, rendererData->vertexShaderModules[i], NULL);
630 rendererData->vertexShaderModules[i] = VK_NULL_HANDLE;
631 }
632 if (rendererData->fragmentShaderModules[i] != VK_NULL_HANDLE) {
633 vkDestroyShaderModule(rendererData->device, rendererData->fragmentShaderModules[i], NULL);
634 rendererData->fragmentShaderModules[i] = VK_NULL_HANDLE;
635 }
636 }
637 if (rendererData->descriptorSetLayout != VK_NULL_HANDLE) {
638 vkDestroyDescriptorSetLayout(rendererData->device, rendererData->descriptorSetLayout, NULL);
639 rendererData->descriptorSetLayout = VK_NULL_HANDLE;
640 }
641 if (rendererData->pipelineLayout != VK_NULL_HANDLE) {
642 vkDestroyPipelineLayout(rendererData->device, rendererData->pipelineLayout, NULL);
643 rendererData->pipelineLayout = VK_NULL_HANDLE;
644 }
645 for (int i = 0; i < rendererData->pipelineStateCount; i++) {
646 vkDestroyPipeline(rendererData->device, rendererData->pipelineStates[i].pipeline, NULL);
647 }
648 SDL_free(rendererData->pipelineStates);
649 rendererData->pipelineStates = NULL;
650 rendererData->pipelineStateCount = 0;
651
652 if (rendererData->currentUploadBuffer) {
653 for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) {
654 for (int j = 0; j < rendererData->currentUploadBuffer[i]; ++j) {
655 VULKAN_DestroyBuffer(rendererData, &rendererData->uploadBuffers[i][j]);
656 }
657 SDL_free(rendererData->uploadBuffers[i]);
658 }
659 SDL_free(rendererData->uploadBuffers);
660 rendererData->uploadBuffers = NULL;
661 SDL_free(rendererData->currentUploadBuffer);
662 rendererData->currentUploadBuffer = NULL;
663 }
664
665 if (rendererData->constantBuffers) {
666 SDL_assert(rendererData->numConstantBuffers);
667 for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) {
668 for (uint32_t j = 0; j < rendererData->numConstantBuffers[i]; j++) {
669 VULKAN_DestroyBuffer(rendererData, &rendererData->constantBuffers[i][j]);
670 }
671 SDL_free(rendererData->constantBuffers[i]);
672 }
673 SDL_free(rendererData->constantBuffers);
674 rendererData->constantBuffers = NULL;
675 SDL_free(rendererData->numConstantBuffers);
676 rendererData->numConstantBuffers = NULL;
677 }
678
679 if (rendererData->device != VK_NULL_HANDLE && !rendererData->device_external) {
680 vkDestroyDevice(rendererData->device, NULL);
681 rendererData->device = VK_NULL_HANDLE;
682 }
683 if (rendererData->surface != VK_NULL_HANDLE && !rendererData->surface_external) {
684 vkDestroySurfaceKHR(rendererData->instance, rendererData->surface, NULL);
685 rendererData->surface = VK_NULL_HANDLE;
686 }
687 if (rendererData->instance != VK_NULL_HANDLE && !rendererData->instance_external) {
688 vkDestroyInstance(rendererData->instance, NULL);
689 rendererData->instance = VK_NULL_HANDLE;
690 }
691}
692
693static void VULKAN_DestroyBuffer(VULKAN_RenderData *rendererData, VULKAN_Buffer *vulkanBuffer)
694{
695 if (vulkanBuffer->buffer != VK_NULL_HANDLE) {
696 vkDestroyBuffer(rendererData->device, vulkanBuffer->buffer, NULL);
697 vulkanBuffer->buffer = VK_NULL_HANDLE;
698 }
699 if (vulkanBuffer->deviceMemory != VK_NULL_HANDLE) {
700 vkFreeMemory(rendererData->device, vulkanBuffer->deviceMemory, NULL);
701 vulkanBuffer->deviceMemory = VK_NULL_HANDLE;
702 }
703 SDL_memset(vulkanBuffer, 0, sizeof(VULKAN_Buffer));
704}
705
706static VkResult VULKAN_AllocateBuffer(VULKAN_RenderData *rendererData, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags requiredMemoryProps, VkMemoryPropertyFlags desiredMemoryProps, VULKAN_Buffer *bufferOut)
707{
708 VkResult result;
709 VkBufferCreateInfo bufferCreateInfo = { 0 };
710 bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
711 bufferCreateInfo.size = size;
712 bufferCreateInfo.usage = usage;
713 result = vkCreateBuffer(rendererData->device, &bufferCreateInfo, NULL, &bufferOut->buffer);
714 if (result != VK_SUCCESS) {
715 SET_ERROR_CODE("vkCreateBuffer()", result);
716 return result;
717 }
718
719 VkMemoryRequirements memoryRequirements = { 0 };
720 vkGetBufferMemoryRequirements(rendererData->device, bufferOut->buffer, &memoryRequirements);
721 if (result != VK_SUCCESS) {
722 VULKAN_DestroyBuffer(rendererData, bufferOut);
723 SET_ERROR_CODE("vkGetBufferMemoryRequirements()", result);
724 return result;
725 }
726
727 uint32_t memoryTypeIndex = 0;
728 if (!VULKAN_FindMemoryTypeIndex(rendererData, memoryRequirements.memoryTypeBits, requiredMemoryProps, desiredMemoryProps, &memoryTypeIndex)) {
729 VULKAN_DestroyBuffer(rendererData, bufferOut);
730 return VK_ERROR_UNKNOWN;
731 }
732
733 VkMemoryAllocateInfo memoryAllocateInfo = { 0 };
734 memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
735 memoryAllocateInfo.allocationSize = memoryRequirements.size;
736 memoryAllocateInfo.memoryTypeIndex = memoryTypeIndex;
737 result = vkAllocateMemory(rendererData->device, &memoryAllocateInfo, NULL, &bufferOut->deviceMemory);
738 if (result != VK_SUCCESS) {
739 VULKAN_DestroyBuffer(rendererData, bufferOut);
740 SET_ERROR_CODE("vkAllocateMemory()", result);
741 return result;
742 }
743 result = vkBindBufferMemory(rendererData->device, bufferOut->buffer, bufferOut->deviceMemory, 0);
744 if (result != VK_SUCCESS) {
745 VULKAN_DestroyBuffer(rendererData, bufferOut);
746 SET_ERROR_CODE("vkBindBufferMemory()", result);
747 return result;
748 }
749
750 result = vkMapMemory(rendererData->device, bufferOut->deviceMemory, 0, size, 0, &bufferOut->mappedBufferPtr);
751 if (result != VK_SUCCESS) {
752 VULKAN_DestroyBuffer(rendererData, bufferOut);
753 SET_ERROR_CODE("vkMapMemory()", result);
754 return result;
755 }
756 bufferOut->size = size;
757 return result;
758}
759
760static void VULKAN_DestroyImage(VULKAN_RenderData *rendererData, VULKAN_Image *vulkanImage)
761{
762 if (vulkanImage->imageView != VK_NULL_HANDLE) {
763 vkDestroyImageView(rendererData->device, vulkanImage->imageView, NULL);
764 vulkanImage->imageView = VK_NULL_HANDLE;
765 }
766 if (vulkanImage->image != VK_NULL_HANDLE) {
767 if (vulkanImage->allocatedImage) {
768 vkDestroyImage(rendererData->device, vulkanImage->image, NULL);
769 }
770 vulkanImage->image = VK_NULL_HANDLE;
771 }
772
773 if (vulkanImage->deviceMemory != VK_NULL_HANDLE) {
774 if (vulkanImage->allocatedImage) {
775 vkFreeMemory(rendererData->device, vulkanImage->deviceMemory, NULL);
776 }
777 vulkanImage->deviceMemory = VK_NULL_HANDLE;
778 }
779 SDL_memset(vulkanImage, 0, sizeof(VULKAN_Image));
780}
781
782static VkResult VULKAN_AllocateImage(VULKAN_RenderData *rendererData, SDL_PropertiesID create_props, uint32_t width, uint32_t height, VkFormat format, VkImageUsageFlags imageUsage, VkComponentMapping swizzle, VkSamplerYcbcrConversionKHR samplerYcbcrConversion, VULKAN_Image *imageOut)
783{
784 VkResult result;
785 VkSamplerYcbcrConversionInfoKHR samplerYcbcrConversionInfo = { 0 };
786
787 SDL_memset(imageOut, 0, sizeof(VULKAN_Image));
788 imageOut->format = format;
789 imageOut->image = (VkImage)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER, 0);
790
791 if (imageOut->image == VK_NULL_HANDLE) {
792 imageOut->allocatedImage = VK_TRUE;
793
794 VkImageCreateInfo imageCreateInfo = { 0 };
795 imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
796 imageCreateInfo.flags = 0;
797 imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
798 imageCreateInfo.format = format;
799 imageCreateInfo.extent.width = width;
800 imageCreateInfo.extent.height = height;
801 imageCreateInfo.extent.depth = 1;
802 imageCreateInfo.mipLevels = 1;
803 imageCreateInfo.arrayLayers = 1;
804 imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
805 imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
806 imageCreateInfo.usage = imageUsage;
807 imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
808 imageCreateInfo.queueFamilyIndexCount = 0;
809 imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
810
811 result = vkCreateImage(rendererData->device, &imageCreateInfo, NULL, &imageOut->image);
812 if (result != VK_SUCCESS) {
813 VULKAN_DestroyImage(rendererData, imageOut);
814 SET_ERROR_CODE("vkCreateImage()", result);
815 return result;
816 }
817
818 VkMemoryRequirements memoryRequirements = { 0 };
819 vkGetImageMemoryRequirements(rendererData->device, imageOut->image, &memoryRequirements);
820 if (result != VK_SUCCESS) {
821 VULKAN_DestroyImage(rendererData, imageOut);
822 SET_ERROR_CODE("vkGetImageMemoryRequirements()", result);
823 return result;
824 }
825
826 uint32_t memoryTypeIndex = 0;
827 if (!VULKAN_FindMemoryTypeIndex(rendererData, memoryRequirements.memoryTypeBits, 0, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memoryTypeIndex)) {
828 VULKAN_DestroyImage(rendererData, imageOut);
829 return VK_ERROR_UNKNOWN;
830 }
831
832 VkMemoryAllocateInfo memoryAllocateInfo = { 0 };
833 memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
834 memoryAllocateInfo.allocationSize = memoryRequirements.size;
835 memoryAllocateInfo.memoryTypeIndex = memoryTypeIndex;
836 result = vkAllocateMemory(rendererData->device, &memoryAllocateInfo, NULL, &imageOut->deviceMemory);
837 if (result != VK_SUCCESS) {
838 VULKAN_DestroyImage(rendererData, imageOut);
839 SET_ERROR_CODE("vkAllocateMemory()", result);
840 return result;
841 }
842 result = vkBindImageMemory(rendererData->device, imageOut->image, imageOut->deviceMemory, 0);
843 if (result != VK_SUCCESS) {
844 VULKAN_DestroyImage(rendererData, imageOut);
845 SET_ERROR_CODE("vkBindImageMemory()", result);
846 return result;
847 }
848 } else {
849 imageOut->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
850 }
851
852 VkImageViewCreateInfo imageViewCreateInfo = { 0 };
853 imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
854 imageViewCreateInfo.image = imageOut->image;
855 imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
856 imageViewCreateInfo.format = format;
857 imageViewCreateInfo.components = swizzle;
858 imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
859 imageViewCreateInfo.subresourceRange.baseMipLevel = 0;
860 imageViewCreateInfo.subresourceRange.levelCount = 1;
861 imageViewCreateInfo.subresourceRange.baseArrayLayer = 0;
862 imageViewCreateInfo.subresourceRange.layerCount = 1;
863
864 // If it's a YCbCr image, we need to pass the conversion info to the VkImageView (and the VkSampler)
865 if (samplerYcbcrConversion != VK_NULL_HANDLE) {
866 samplerYcbcrConversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR;
867 samplerYcbcrConversionInfo.conversion = samplerYcbcrConversion;
868 imageViewCreateInfo.pNext = &samplerYcbcrConversionInfo;
869 }
870
871 result = vkCreateImageView(rendererData->device, &imageViewCreateInfo, NULL, &imageOut->imageView);
872 if (result != VK_SUCCESS) {
873 VULKAN_DestroyImage(rendererData, imageOut);
874 SET_ERROR_CODE("vkCreateImageView()", result);
875 return result;
876 }
877
878 return result;
879}
880
881
882static void VULKAN_RecordPipelineImageBarrier(VULKAN_RenderData *rendererData, VkAccessFlags sourceAccessMask, VkAccessFlags destAccessMask,
883 VkPipelineStageFlags srcStageFlags, VkPipelineStageFlags dstStageFlags, VkImageLayout destLayout, VkImage image, VkImageLayout *imageLayout)
884{
885 // Stop any outstanding renderpass if open
886 if (rendererData->currentRenderPass != VK_NULL_HANDLE) {
887 vkCmdEndRenderPass(rendererData->currentCommandBuffer);
888 rendererData->currentRenderPass = VK_NULL_HANDLE;
889 }
890
891 VkImageMemoryBarrier barrier = { 0 };
892 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
893 barrier.srcAccessMask = sourceAccessMask;
894 barrier.dstAccessMask = destAccessMask;
895 barrier.oldLayout = *imageLayout;
896 barrier.newLayout = destLayout;
897 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
898 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
899 barrier.image = image;
900 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
901 barrier.subresourceRange.baseMipLevel = 0;
902 barrier.subresourceRange.levelCount = 1;
903 barrier.subresourceRange.baseArrayLayer = 0;
904 barrier.subresourceRange.layerCount = 1;
905 vkCmdPipelineBarrier(rendererData->currentCommandBuffer, srcStageFlags, dstStageFlags, 0, 0, NULL, 0, NULL, 1, &barrier);
906 *imageLayout = destLayout;
907}
908
909static VkResult VULKAN_AcquireNextSwapchainImage(SDL_Renderer *renderer)
910{
911 VULKAN_RenderData *rendererData = ( VULKAN_RenderData * )renderer->internal;
912
913 VkResult result;
914
915 rendererData->currentImageAvailableSemaphore = VK_NULL_HANDLE;
916 result = vkAcquireNextImageKHR(rendererData->device, rendererData->swapchain, UINT64_MAX,
917 rendererData->imageAvailableSemaphores[rendererData->currentCommandBufferIndex], VK_NULL_HANDLE, &rendererData->currentSwapchainImageIndex);
918 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_ERROR_SURFACE_LOST_KHR) {
919 result = VULKAN_CreateWindowSizeDependentResources(renderer);
920 return result;
921 } else if(result == VK_SUBOPTIMAL_KHR) {
922 // Suboptimal, but we can contiue
923 } else if (result != VK_SUCCESS) {
924 SET_ERROR_CODE("vkAcquireNextImageKHR()", result);
925 return result;
926 }
927 rendererData->currentImageAvailableSemaphore = rendererData->imageAvailableSemaphores[rendererData->currentCommandBufferIndex];
928 return result;
929}
930
931static void VULKAN_BeginRenderPass(VULKAN_RenderData *rendererData, VkAttachmentLoadOp loadOp, VkClearColorValue *clearColor)
932{
933 int width = rendererData->swapchainSize.width;
934 int height = rendererData->swapchainSize.height;
935 if (rendererData->textureRenderTarget) {
936 width = rendererData->textureRenderTarget->width;
937 height = rendererData->textureRenderTarget->height;
938 }
939
940 switch (loadOp) {
941 case VK_ATTACHMENT_LOAD_OP_CLEAR:
942 rendererData->currentRenderPass = rendererData->textureRenderTarget ?
943 rendererData->textureRenderTarget->mainRenderpasses[VULKAN_RENDERPASS_CLEAR] :
944 rendererData->renderPasses[VULKAN_RENDERPASS_CLEAR];
945 break;
946
947 case VK_ATTACHMENT_LOAD_OP_LOAD:
948 default:
949 rendererData->currentRenderPass = rendererData->textureRenderTarget ?
950 rendererData->textureRenderTarget->mainRenderpasses[VULKAN_RENDERPASS_LOAD] :
951 rendererData->renderPasses[VULKAN_RENDERPASS_LOAD];
952 break;
953 }
954
955 VkFramebuffer framebuffer = rendererData->textureRenderTarget ?
956 rendererData->textureRenderTarget->mainFramebuffer :
957 rendererData->framebuffers[rendererData->currentSwapchainImageIndex];
958
959 VkRenderPassBeginInfo renderPassBeginInfo = { 0 };
960 renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
961 renderPassBeginInfo.pNext = NULL;
962 renderPassBeginInfo.renderPass = rendererData->currentRenderPass;
963 renderPassBeginInfo.framebuffer = framebuffer;
964 renderPassBeginInfo.renderArea.offset.x = 0;
965 renderPassBeginInfo.renderArea.offset.y = 0;
966 renderPassBeginInfo.renderArea.extent.width = width;
967 renderPassBeginInfo.renderArea.extent.height = height;
968 renderPassBeginInfo.clearValueCount = (clearColor == NULL) ? 0 : 1;
969 VkClearValue clearValue;
970 if (clearColor != NULL) {
971 clearValue.color = *clearColor;
972 renderPassBeginInfo.pClearValues = &clearValue;
973 }
974 vkCmdBeginRenderPass(rendererData->currentCommandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
975}
976
977static void VULKAN_EnsureCommandBuffer(VULKAN_RenderData *rendererData)
978{
979 if (rendererData->currentCommandBuffer == VK_NULL_HANDLE) {
980 rendererData->currentCommandBuffer = rendererData->commandBuffers[rendererData->currentCommandBufferIndex];
981 VULKAN_ResetCommandList(rendererData);
982
983 // Ensure the swapchain is in the correct layout
984 if (rendererData->swapchainImageLayouts[rendererData->currentSwapchainImageIndex] == VK_IMAGE_LAYOUT_UNDEFINED) {
985 VULKAN_RecordPipelineImageBarrier(rendererData,
986 0,
987 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
988 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
989 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
990 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
991 rendererData->swapchainImages[rendererData->currentSwapchainImageIndex],
992 &rendererData->swapchainImageLayouts[rendererData->currentSwapchainImageIndex]);
993 }
994 else if (rendererData->swapchainImageLayouts[rendererData->currentCommandBufferIndex] != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
995 VULKAN_RecordPipelineImageBarrier(rendererData,
996 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT,
997 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
998 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
999 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
1000 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
1001 rendererData->swapchainImages[rendererData->currentSwapchainImageIndex],
1002 &rendererData->swapchainImageLayouts[rendererData->currentSwapchainImageIndex]);
1003 }
1004 }
1005}
1006
1007static bool VULKAN_ActivateCommandBuffer(SDL_Renderer *renderer, VkAttachmentLoadOp loadOp, VkClearColorValue *clearColor, VULKAN_DrawStateCache *stateCache)
1008{
1009 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
1010
1011 VULKAN_EnsureCommandBuffer(rendererData);
1012
1013 if (rendererData->currentRenderPass == VK_NULL_HANDLE || loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR) {
1014 if (rendererData->currentRenderPass != VK_NULL_HANDLE) {
1015 vkCmdEndRenderPass(rendererData->currentCommandBuffer);
1016 rendererData->currentRenderPass = VK_NULL_HANDLE;
1017 }
1018 VULKAN_BeginRenderPass(rendererData, loadOp, clearColor);
1019 }
1020
1021 // Bind cached VB now
1022 if (stateCache->vertexBuffer != VK_NULL_HANDLE) {
1023 VkDeviceSize offset = 0;
1024 vkCmdBindVertexBuffers(rendererData->currentCommandBuffer, 0, 1, &stateCache->vertexBuffer, &offset);
1025 }
1026
1027 return true;
1028}
1029
1030static void VULKAN_WaitForGPU(VULKAN_RenderData *rendererData)
1031{
1032 vkQueueWaitIdle(rendererData->graphicsQueue);
1033}
1034
1035
1036static void VULKAN_ResetCommandList(VULKAN_RenderData *rendererData)
1037{
1038 vkResetCommandBuffer(rendererData->currentCommandBuffer, 0);
1039 for (uint32_t i = 0; i < rendererData->numDescriptorPools[rendererData->currentCommandBufferIndex]; i++) {
1040 vkResetDescriptorPool(rendererData->device, rendererData->descriptorPools[rendererData->currentCommandBufferIndex][i], 0);
1041 }
1042
1043 VkCommandBufferBeginInfo beginInfo = { 0 };
1044 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1045 beginInfo.flags = 0;
1046 vkBeginCommandBuffer(rendererData->currentCommandBuffer, &beginInfo);
1047
1048 rendererData->currentPipelineState = NULL;
1049 rendererData->currentVertexBuffer = 0;
1050 rendererData->issueBatch = false;
1051 rendererData->cliprectDirty = true;
1052 rendererData->currentDescriptorSetIndex = 0;
1053 rendererData->currentDescriptorPoolIndex = 0;
1054 rendererData->currentConstantBufferOffset = -1;
1055 rendererData->currentConstantBufferIndex = 0;
1056
1057 // Release any upload buffers that were inflight
1058 for (int i = 0; i < rendererData->currentUploadBuffer[rendererData->currentCommandBufferIndex]; ++i) {
1059 VULKAN_DestroyBuffer(rendererData, &rendererData->uploadBuffers[rendererData->currentCommandBufferIndex][i]);
1060 }
1061 rendererData->currentUploadBuffer[rendererData->currentCommandBufferIndex] = 0;
1062}
1063
1064static VkResult VULKAN_IssueBatch(VULKAN_RenderData *rendererData)
1065{
1066 VkResult result;
1067 if (rendererData->currentCommandBuffer == VK_NULL_HANDLE) {
1068 return VK_SUCCESS;
1069 }
1070
1071 if (rendererData->currentRenderPass) {
1072 vkCmdEndRenderPass(rendererData->currentCommandBuffer);
1073 rendererData->currentRenderPass = VK_NULL_HANDLE;
1074 }
1075
1076 rendererData->currentPipelineState = VK_NULL_HANDLE;
1077 rendererData->viewportDirty = true;
1078
1079 vkEndCommandBuffer(rendererData->currentCommandBuffer);
1080
1081 VkSubmitInfo submitInfo = { 0 };
1082 VkPipelineStageFlags waitDestStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
1083 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1084 submitInfo.commandBufferCount = 1;
1085 submitInfo.pCommandBuffers = &rendererData->currentCommandBuffer;
1086 if (rendererData->waitRenderSemaphoreCount > 0) {
1087 Uint32 additionalSemaphoreCount = (rendererData->currentImageAvailableSemaphore != VK_NULL_HANDLE) ? 1 : 0;
1088 submitInfo.waitSemaphoreCount = rendererData->waitRenderSemaphoreCount + additionalSemaphoreCount;
1089 if (additionalSemaphoreCount > 0) {
1090 rendererData->waitRenderSemaphores[rendererData->waitRenderSemaphoreCount] = rendererData->currentImageAvailableSemaphore;
1091 rendererData->waitDestStageMasks[rendererData->waitRenderSemaphoreCount] = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
1092 }
1093 submitInfo.pWaitSemaphores = rendererData->waitRenderSemaphores;
1094 submitInfo.pWaitDstStageMask = rendererData->waitDestStageMasks;
1095 rendererData->waitRenderSemaphoreCount = 0;
1096 } else if (rendererData->currentImageAvailableSemaphore != VK_NULL_HANDLE) {
1097 submitInfo.waitSemaphoreCount = 1;
1098 submitInfo.pWaitSemaphores = &rendererData->currentImageAvailableSemaphore;
1099 submitInfo.pWaitDstStageMask = &waitDestStageMask;
1100 }
1101
1102 result = vkQueueSubmit(rendererData->graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
1103 rendererData->currentImageAvailableSemaphore = VK_NULL_HANDLE;
1104
1105 VULKAN_WaitForGPU(rendererData);
1106
1107 VULKAN_ResetCommandList(rendererData);
1108
1109 return result;
1110}
1111
1112static void VULKAN_DestroyRenderer(SDL_Renderer *renderer)
1113{
1114 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
1115 if (rendererData) {
1116 if (rendererData->device != VK_NULL_HANDLE) {
1117 vkDeviceWaitIdle(rendererData->device);
1118 VULKAN_DestroyAll(renderer);
1119 }
1120 SDL_free(rendererData);
1121 }
1122}
1123
1124static VkBlendFactor GetBlendFactor(SDL_BlendFactor factor)
1125{
1126 switch (factor) {
1127 case SDL_BLENDFACTOR_ZERO:
1128 return VK_BLEND_FACTOR_ZERO;
1129 case SDL_BLENDFACTOR_ONE:
1130 return VK_BLEND_FACTOR_ONE;
1131 case SDL_BLENDFACTOR_SRC_COLOR:
1132 return VK_BLEND_FACTOR_SRC_COLOR;
1133 case SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR:
1134 return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
1135 case SDL_BLENDFACTOR_SRC_ALPHA:
1136 return VK_BLEND_FACTOR_SRC_ALPHA;
1137 case SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA:
1138 return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
1139 case SDL_BLENDFACTOR_DST_COLOR:
1140 return VK_BLEND_FACTOR_DST_COLOR;
1141 case SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR:
1142 return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
1143 case SDL_BLENDFACTOR_DST_ALPHA:
1144 return VK_BLEND_FACTOR_DST_ALPHA;
1145 case SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA:
1146 return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
1147 default:
1148 return VK_BLEND_FACTOR_MAX_ENUM;
1149 }
1150}
1151
1152static VkBlendOp GetBlendOp(SDL_BlendOperation operation)
1153{
1154 switch (operation) {
1155 case SDL_BLENDOPERATION_ADD:
1156 return VK_BLEND_OP_ADD;
1157 case SDL_BLENDOPERATION_SUBTRACT:
1158 return VK_BLEND_OP_SUBTRACT;
1159 case SDL_BLENDOPERATION_REV_SUBTRACT:
1160 return VK_BLEND_OP_REVERSE_SUBTRACT;
1161 case SDL_BLENDOPERATION_MINIMUM:
1162 return VK_BLEND_OP_MIN;
1163 case SDL_BLENDOPERATION_MAXIMUM:
1164 return VK_BLEND_OP_MAX;
1165 default:
1166 return VK_BLEND_OP_MAX_ENUM;
1167 }
1168}
1169
1170
1171static VULKAN_PipelineState *VULKAN_CreatePipelineState(SDL_Renderer *renderer,
1172 VULKAN_Shader shader, VkPipelineLayout pipelineLayout, VkDescriptorSetLayout descriptorSetLayout, SDL_BlendMode blendMode, VkPrimitiveTopology topology, VkFormat format)
1173{
1174 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
1175 VULKAN_PipelineState *pipelineStates;
1176 VkPipeline pipeline = VK_NULL_HANDLE;
1177 VkResult result = VK_SUCCESS;
1178 VkPipelineVertexInputStateCreateInfo vertexInputCreateInfo = { 0 };
1179 VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = { 0 };
1180 VkVertexInputAttributeDescription attributeDescriptions[3];
1181 VkVertexInputBindingDescription bindingDescriptions[1];
1182 VkPipelineShaderStageCreateInfo shaderStageCreateInfo[2];
1183 VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo = { 0 };
1184 VkPipelineViewportStateCreateInfo viewportStateCreateInfo = { 0 };
1185 VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo = { 0 };
1186 VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo = { 0 };
1187 VkPipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = { 0 };
1188 VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo = { 0 };
1189
1190 VkGraphicsPipelineCreateInfo pipelineCreateInfo = { 0 };
1191 pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
1192 pipelineCreateInfo.flags = 0;
1193 pipelineCreateInfo.pStages = shaderStageCreateInfo;
1194 pipelineCreateInfo.pVertexInputState = &vertexInputCreateInfo;
1195 pipelineCreateInfo.pInputAssemblyState = &inputAssemblyStateCreateInfo;
1196 pipelineCreateInfo.pViewportState = &viewportStateCreateInfo;
1197 pipelineCreateInfo.pRasterizationState = &rasterizationStateCreateInfo;
1198 pipelineCreateInfo.pMultisampleState = &multisampleStateCreateInfo;
1199 pipelineCreateInfo.pDepthStencilState = &depthStencilStateCreateInfo;
1200 pipelineCreateInfo.pColorBlendState = &colorBlendStateCreateInfo;
1201 pipelineCreateInfo.pDynamicState = &dynamicStateCreateInfo;
1202
1203 // Shaders
1204 const char *name = "main";
1205 for (uint32_t i = 0; i < 2; i++) {
1206 SDL_memset(&shaderStageCreateInfo[i], 0, sizeof(shaderStageCreateInfo[i]));
1207 shaderStageCreateInfo[i].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1208 shaderStageCreateInfo[i].module = (i == 0) ? rendererData->vertexShaderModules[shader] : rendererData->fragmentShaderModules[shader];
1209 shaderStageCreateInfo[i].stage = (i == 0) ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT;
1210 shaderStageCreateInfo[i].pName = name;
1211 }
1212 pipelineCreateInfo.stageCount = 2;
1213 pipelineCreateInfo.pStages = &shaderStageCreateInfo[0];
1214
1215
1216 // Vertex input
1217 vertexInputCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
1218 vertexInputCreateInfo.vertexAttributeDescriptionCount = 3;
1219 vertexInputCreateInfo.pVertexAttributeDescriptions = &attributeDescriptions[0];
1220 vertexInputCreateInfo.vertexBindingDescriptionCount = 1;
1221 vertexInputCreateInfo.pVertexBindingDescriptions = &bindingDescriptions[0];
1222
1223 attributeDescriptions[ 0 ].binding = 0;
1224 attributeDescriptions[ 0 ].format = VK_FORMAT_R32G32_SFLOAT;
1225 attributeDescriptions[ 0 ].location = 0;
1226 attributeDescriptions[ 0 ].offset = 0;
1227 attributeDescriptions[ 1 ].binding = 0;
1228 attributeDescriptions[ 1 ].format = VK_FORMAT_R32G32_SFLOAT;
1229 attributeDescriptions[ 1 ].location = 1;
1230 attributeDescriptions[ 1 ].offset = 8;
1231 attributeDescriptions[ 2 ].binding = 0;
1232 attributeDescriptions[ 2 ].format = VK_FORMAT_R32G32B32A32_SFLOAT;
1233 attributeDescriptions[ 2 ].location = 2;
1234 attributeDescriptions[ 2 ].offset = 16;
1235
1236 bindingDescriptions[ 0 ].binding = 0;
1237 bindingDescriptions[ 0 ].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
1238 bindingDescriptions[ 0 ].stride = 32;
1239
1240 // Input assembly
1241 inputAssemblyStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
1242 inputAssemblyStateCreateInfo.topology = topology;
1243 inputAssemblyStateCreateInfo.primitiveRestartEnable = VK_FALSE;
1244
1245 viewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
1246 viewportStateCreateInfo.scissorCount = 1;
1247 viewportStateCreateInfo.viewportCount = 1;
1248
1249 // Dynamic states
1250 dynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
1251 VkDynamicState dynamicStates[2] = {
1252 VK_DYNAMIC_STATE_VIEWPORT,
1253 VK_DYNAMIC_STATE_SCISSOR
1254 };
1255 dynamicStateCreateInfo.dynamicStateCount = SDL_arraysize(dynamicStates);
1256 dynamicStateCreateInfo.pDynamicStates = dynamicStates;
1257
1258 // Rasterization state
1259 rasterizationStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
1260 rasterizationStateCreateInfo.depthClampEnable = VK_FALSE;
1261 rasterizationStateCreateInfo.rasterizerDiscardEnable = VK_FALSE;
1262 rasterizationStateCreateInfo.cullMode = VK_CULL_MODE_NONE;
1263 rasterizationStateCreateInfo.polygonMode = VK_POLYGON_MODE_FILL;
1264 rasterizationStateCreateInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
1265 rasterizationStateCreateInfo.depthBiasEnable = VK_FALSE;
1266 rasterizationStateCreateInfo.depthBiasConstantFactor = 0.0f;
1267 rasterizationStateCreateInfo.depthBiasClamp = 0.0f;
1268 rasterizationStateCreateInfo.depthBiasSlopeFactor = 0.0f;
1269 rasterizationStateCreateInfo.lineWidth = 1.0f;
1270
1271 // MSAA state
1272 multisampleStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
1273 VkSampleMask multiSampleMask = 0xFFFFFFFF;
1274 multisampleStateCreateInfo.pSampleMask = &multiSampleMask;
1275 multisampleStateCreateInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
1276
1277 // Depth Stencil
1278 depthStencilStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
1279
1280 // Color blend
1281 VkPipelineColorBlendAttachmentState colorBlendAttachment = { 0 };
1282 colorBlendStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
1283 colorBlendStateCreateInfo.attachmentCount = 1;
1284 colorBlendStateCreateInfo.pAttachments = &colorBlendAttachment;
1285 colorBlendAttachment.blendEnable = VK_TRUE;
1286 colorBlendAttachment.srcColorBlendFactor = GetBlendFactor(SDL_GetBlendModeSrcColorFactor(blendMode));
1287 colorBlendAttachment.srcAlphaBlendFactor = GetBlendFactor(SDL_GetBlendModeSrcAlphaFactor(blendMode));
1288 colorBlendAttachment.colorBlendOp = GetBlendOp(SDL_GetBlendModeColorOperation(blendMode));
1289 colorBlendAttachment.dstColorBlendFactor = GetBlendFactor(SDL_GetBlendModeDstColorFactor(blendMode));
1290 colorBlendAttachment.dstAlphaBlendFactor = GetBlendFactor(SDL_GetBlendModeDstAlphaFactor(blendMode));
1291 colorBlendAttachment.alphaBlendOp = GetBlendOp(SDL_GetBlendModeAlphaOperation(blendMode));
1292 colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
1293
1294 // Renderpass / layout
1295 pipelineCreateInfo.renderPass = rendererData->currentRenderPass;
1296 pipelineCreateInfo.subpass = 0;
1297 pipelineCreateInfo.layout = pipelineLayout;
1298
1299 result = vkCreateGraphicsPipelines(rendererData->device, VK_NULL_HANDLE, 1, &pipelineCreateInfo, NULL, &pipeline);
1300 if (result != VK_SUCCESS) {
1301 SET_ERROR_CODE("vkCreateGraphicsPipelines()", result);
1302 return NULL;
1303 }
1304
1305 pipelineStates = (VULKAN_PipelineState *)SDL_realloc(rendererData->pipelineStates, (rendererData->pipelineStateCount + 1) * sizeof(*pipelineStates));
1306 if (!pipelineStates) {
1307 return NULL;
1308 }
1309 pipelineStates[rendererData->pipelineStateCount].shader = shader;
1310 pipelineStates[rendererData->pipelineStateCount].blendMode = blendMode;
1311 pipelineStates[rendererData->pipelineStateCount].topology = topology;
1312 pipelineStates[rendererData->pipelineStateCount].format = format;
1313 pipelineStates[rendererData->pipelineStateCount].pipeline = pipeline;
1314 pipelineStates[rendererData->pipelineStateCount].descriptorSetLayout = descriptorSetLayout;
1315 pipelineStates[rendererData->pipelineStateCount].pipelineLayout = pipelineCreateInfo.layout;
1316 rendererData->pipelineStates = pipelineStates;
1317 ++rendererData->pipelineStateCount;
1318
1319 return &pipelineStates[rendererData->pipelineStateCount - 1];
1320}
1321
1322static bool VULKAN_FindMemoryTypeIndex(VULKAN_RenderData *rendererData, uint32_t typeBits, VkMemoryPropertyFlags requiredFlags, VkMemoryPropertyFlags desiredFlags, uint32_t *memoryTypeIndexOut)
1323{
1324 uint32_t memoryTypeIndex = 0;
1325 bool foundExactMatch = false;
1326
1327 // Desired flags must be a superset of required flags.
1328 desiredFlags |= requiredFlags;
1329
1330 for (memoryTypeIndex = 0; memoryTypeIndex < rendererData->physicalDeviceMemoryProperties.memoryTypeCount; memoryTypeIndex++) {
1331 if (typeBits & (1 << memoryTypeIndex)) {
1332 if (rendererData->physicalDeviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags == desiredFlags) {
1333 foundExactMatch = true;
1334 break;
1335 }
1336 }
1337 }
1338 if (!foundExactMatch) {
1339 for (memoryTypeIndex = 0; memoryTypeIndex < rendererData->physicalDeviceMemoryProperties.memoryTypeCount; memoryTypeIndex++) {
1340 if (typeBits & (1 << memoryTypeIndex)) {
1341 if ((rendererData->physicalDeviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags & requiredFlags) == requiredFlags) {
1342 break;
1343 }
1344 }
1345 }
1346 }
1347
1348 if (memoryTypeIndex >= rendererData->physicalDeviceMemoryProperties.memoryTypeCount) {
1349 SET_ERROR_MESSAGE("Unable to find memory type for allocation");
1350 return false;
1351 }
1352 *memoryTypeIndexOut = memoryTypeIndex;
1353 return true;
1354}
1355
1356static VkResult VULKAN_CreateVertexBuffer(VULKAN_RenderData *rendererData, size_t vbidx, size_t size)
1357{
1358 VkResult result;
1359
1360 VULKAN_DestroyBuffer(rendererData, &rendererData->vertexBuffers[vbidx]);
1361
1362 result = VULKAN_AllocateBuffer(rendererData, size,
1363 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
1364 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
1365 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
1366 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
1367 &rendererData->vertexBuffers[vbidx]);
1368 if (result != VK_SUCCESS) {
1369 return result;
1370 }
1371 return result;
1372}
1373
1374static bool VULKAN_LoadGlobalFunctions(VULKAN_RenderData *rendererData)
1375{
1376#define VULKAN_DEVICE_FUNCTION(name)
1377#define VULKAN_GLOBAL_FUNCTION(name) \
1378 name = (PFN_##name)rendererData->vkGetInstanceProcAddr(VK_NULL_HANDLE, #name); \
1379 if (!name) { \
1380 SET_ERROR_MESSAGE("vkGetInstanceProcAddr(VK_NULL_HANDLE, \"" #name "\") failed"); \
1381 return false; \
1382 }
1383#define VULKAN_INSTANCE_FUNCTION(name)
1384#define VULKAN_OPTIONAL_INSTANCE_FUNCTION(name)
1385#define VULKAN_OPTIONAL_DEVICE_FUNCTION(name)
1386 VULKAN_FUNCTIONS()
1387#undef VULKAN_DEVICE_FUNCTION
1388#undef VULKAN_GLOBAL_FUNCTION
1389#undef VULKAN_INSTANCE_FUNCTION
1390#undef VULKAN_OPTIONAL_INSTANCE_FUNCTION
1391#undef VULKAN_OPTIONAL_DEVICE_FUNCTION
1392
1393 return true;
1394}
1395
1396static bool VULKAN_LoadInstanceFunctions(VULKAN_RenderData *rendererData)
1397{
1398#define VULKAN_DEVICE_FUNCTION(name)
1399#define VULKAN_GLOBAL_FUNCTION(name)
1400#define VULKAN_INSTANCE_FUNCTION(name) \
1401 name = (PFN_##name)rendererData->vkGetInstanceProcAddr(rendererData->instance, #name); \
1402 if (!name) { \
1403 SET_ERROR_MESSAGE("vkGetInstanceProcAddr(instance, \"" #name "\") failed"); \
1404 return false; \
1405 }
1406#define VULKAN_OPTIONAL_INSTANCE_FUNCTION(name) \
1407 name = (PFN_##name)rendererData->vkGetInstanceProcAddr(rendererData->instance, #name);
1408#define VULKAN_OPTIONAL_DEVICE_FUNCTION(name)
1409
1410 VULKAN_FUNCTIONS()
1411#undef VULKAN_DEVICE_FUNCTION
1412#undef VULKAN_GLOBAL_FUNCTION
1413#undef VULKAN_INSTANCE_FUNCTION
1414#undef VULKAN_OPTIONAL_INSTANCE_FUNCTION
1415#undef VULKAN_OPTIONAL_DEVICE_FUNCTION
1416
1417 return true;
1418}
1419
1420static bool VULKAN_LoadDeviceFunctions(VULKAN_RenderData *rendererData)
1421{
1422#define VULKAN_DEVICE_FUNCTION(name) \
1423 name = (PFN_##name)vkGetDeviceProcAddr(rendererData->device, #name); \
1424 if (!name) { \
1425 SET_ERROR_MESSAGE("vkGetDeviceProcAddr(device, \"" #name "\") failed"); \
1426 return false; \
1427 }
1428#define VULKAN_GLOBAL_FUNCTION(name)
1429#define VULKAN_OPTIONAL_DEVICE_FUNCTION(name) \
1430 name = (PFN_##name)vkGetDeviceProcAddr(rendererData->device, #name);
1431#define VULKAN_INSTANCE_FUNCTION(name)
1432#define VULKAN_OPTIONAL_INSTANCE_FUNCTION(name)
1433 VULKAN_FUNCTIONS()
1434#undef VULKAN_DEVICE_FUNCTION
1435#undef VULKAN_GLOBAL_FUNCTION
1436#undef VULKAN_INSTANCE_FUNCTION
1437#undef VULKAN_OPTIONAL_INSTANCE_FUNCTION
1438#undef VULKAN_OPTIONAL_DEVICE_FUNCTION
1439 return true;
1440}
1441
1442static VkResult VULKAN_FindPhysicalDevice(VULKAN_RenderData *rendererData)
1443{
1444 uint32_t physicalDeviceCount = 0;
1445 VkPhysicalDevice *physicalDevices;
1446 VkQueueFamilyProperties *queueFamiliesProperties = NULL;
1447 uint32_t queueFamiliesPropertiesAllocatedSize = 0;
1448 VkExtensionProperties *deviceExtensions = NULL;
1449 uint32_t deviceExtensionsAllocatedSize = 0;
1450 uint32_t physicalDeviceIndex;
1451 VkResult result;
1452
1453 result = vkEnumeratePhysicalDevices(rendererData->instance, &physicalDeviceCount, NULL);
1454 if (result != VK_SUCCESS) {
1455 SET_ERROR_CODE("vkEnumeratePhysicalDevices()", result);
1456 return result;
1457 }
1458 if (physicalDeviceCount == 0) {
1459 SET_ERROR_MESSAGE("vkEnumeratePhysicalDevices(): no physical devices");
1460 return VK_ERROR_UNKNOWN;
1461 }
1462 physicalDevices = (VkPhysicalDevice *)SDL_malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount);
1463 result = vkEnumeratePhysicalDevices(rendererData->instance, &physicalDeviceCount, physicalDevices);
1464 if (result != VK_SUCCESS) {
1465 SDL_free(physicalDevices);
1466 SET_ERROR_CODE("vkEnumeratePhysicalDevices()", result);
1467 return result;
1468 }
1469 rendererData->physicalDevice = NULL;
1470 for (physicalDeviceIndex = 0; physicalDeviceIndex < physicalDeviceCount; physicalDeviceIndex++) {
1471 uint32_t queueFamiliesCount = 0;
1472 uint32_t queueFamilyIndex;
1473 uint32_t deviceExtensionCount = 0;
1474 bool hasSwapchainExtension = false;
1475 uint32_t i;
1476
1477 VkPhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex];
1478 vkGetPhysicalDeviceProperties(physicalDevice, &rendererData->physicalDeviceProperties);
1479 if (VK_VERSION_MAJOR(rendererData->physicalDeviceProperties.apiVersion) < 1) {
1480 continue;
1481 }
1482 vkGetPhysicalDeviceMemoryProperties(physicalDevice, &rendererData->physicalDeviceMemoryProperties);
1483 vkGetPhysicalDeviceFeatures(physicalDevice, &rendererData->physicalDeviceFeatures);
1484 vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, NULL);
1485 if (queueFamiliesCount == 0) {
1486 continue;
1487 }
1488 if (queueFamiliesPropertiesAllocatedSize < queueFamiliesCount) {
1489 SDL_free(queueFamiliesProperties);
1490 queueFamiliesPropertiesAllocatedSize = queueFamiliesCount;
1491 queueFamiliesProperties = (VkQueueFamilyProperties *)SDL_malloc(sizeof(VkQueueFamilyProperties) * queueFamiliesPropertiesAllocatedSize);
1492 if (!queueFamiliesProperties) {
1493 SDL_free(physicalDevices);
1494 SDL_free(deviceExtensions);
1495 return VK_ERROR_UNKNOWN;
1496 }
1497 }
1498 vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, queueFamiliesProperties);
1499 rendererData->graphicsQueueFamilyIndex = queueFamiliesCount;
1500 rendererData->presentQueueFamilyIndex = queueFamiliesCount;
1501 for (queueFamilyIndex = 0; queueFamilyIndex < queueFamiliesCount; queueFamilyIndex++) {
1502 VkBool32 supported = 0;
1503
1504 if (queueFamiliesProperties[queueFamilyIndex].queueCount == 0) {
1505 continue;
1506 }
1507
1508 if (queueFamiliesProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
1509 rendererData->graphicsQueueFamilyIndex = queueFamilyIndex;
1510 }
1511
1512 result = vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, rendererData->surface, &supported);
1513 if (result != VK_SUCCESS) {
1514 SDL_free(physicalDevices);
1515 SDL_free(queueFamiliesProperties);
1516 SDL_free(deviceExtensions);
1517 SET_ERROR_CODE("vkGetPhysicalDeviceSurfaceSupportKHR()", result);
1518 return VK_ERROR_UNKNOWN;
1519 }
1520 if (supported) {
1521 rendererData->presentQueueFamilyIndex = queueFamilyIndex;
1522 if (queueFamiliesProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
1523 break; // use this queue because it can present and do graphics
1524 }
1525 }
1526 }
1527
1528 if (rendererData->graphicsQueueFamilyIndex == queueFamiliesCount) { // no good queues found
1529 continue;
1530 }
1531 if (rendererData->presentQueueFamilyIndex == queueFamiliesCount) { // no good queues found
1532 continue;
1533 }
1534 result = vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, NULL);
1535 if (result != VK_SUCCESS) {
1536 SDL_free(physicalDevices);
1537 SDL_free(queueFamiliesProperties);
1538 SDL_free(deviceExtensions);
1539 SET_ERROR_CODE("vkEnumerateDeviceExtensionProperties()", result);
1540 return VK_ERROR_UNKNOWN;
1541 }
1542 if (deviceExtensionCount == 0) {
1543 continue;
1544 }
1545 if (deviceExtensionsAllocatedSize < deviceExtensionCount) {
1546 SDL_free(deviceExtensions);
1547 deviceExtensionsAllocatedSize = deviceExtensionCount;
1548 deviceExtensions = (VkExtensionProperties *)SDL_malloc(sizeof(VkExtensionProperties) * deviceExtensionsAllocatedSize);
1549 if (!deviceExtensions) {
1550 SDL_free(physicalDevices);
1551 SDL_free(queueFamiliesProperties);
1552 return VK_ERROR_UNKNOWN;
1553 }
1554 }
1555 result = vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, deviceExtensions);
1556 if (result != VK_SUCCESS) {
1557 SDL_free(physicalDevices);
1558 SDL_free(queueFamiliesProperties);
1559 SDL_free(deviceExtensions);
1560 SET_ERROR_CODE("vkEnumerateDeviceExtensionProperties()", result);
1561 return result;
1562 }
1563 for (i = 0; i < deviceExtensionCount; i++) {
1564 if (SDL_strcmp(deviceExtensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0) {
1565 hasSwapchainExtension = true;
1566 break;
1567 }
1568 }
1569 if (!hasSwapchainExtension) {
1570 continue;
1571 }
1572 rendererData->physicalDevice = physicalDevice;
1573 break;
1574 }
1575 SDL_free(physicalDevices);
1576 SDL_free(queueFamiliesProperties);
1577 SDL_free(deviceExtensions);
1578 if (!rendererData->physicalDevice) {
1579 SET_ERROR_MESSAGE("No viable physical devices found");
1580 return VK_ERROR_UNKNOWN;
1581 }
1582 return VK_SUCCESS;
1583}
1584
1585static VkResult VULKAN_GetSurfaceFormats(VULKAN_RenderData *rendererData)
1586{
1587 VkResult result = vkGetPhysicalDeviceSurfaceFormatsKHR(rendererData->physicalDevice,
1588 rendererData->surface,
1589 &rendererData->surfaceFormatsCount,
1590 NULL);
1591 if (result != VK_SUCCESS) {
1592 rendererData->surfaceFormatsCount = 0;
1593 SET_ERROR_CODE("vkGetPhysicalDeviceSurfaceFormatsKHR()", result);
1594 return result;
1595 }
1596 if (rendererData->surfaceFormatsCount > rendererData->surfaceFormatsAllocatedCount) {
1597 rendererData->surfaceFormatsAllocatedCount = rendererData->surfaceFormatsCount;
1598 SDL_free(rendererData->surfaceFormats);
1599 rendererData->surfaceFormats = (VkSurfaceFormatKHR *)SDL_malloc(sizeof(VkSurfaceFormatKHR) * rendererData->surfaceFormatsAllocatedCount);
1600 }
1601 result = vkGetPhysicalDeviceSurfaceFormatsKHR(rendererData->physicalDevice,
1602 rendererData->surface,
1603 &rendererData->surfaceFormatsCount,
1604 rendererData->surfaceFormats);
1605 if (result != VK_SUCCESS) {
1606 rendererData->surfaceFormatsCount = 0;
1607 SET_ERROR_CODE("vkGetPhysicalDeviceSurfaceFormatsKHR()", result);
1608 return result;
1609 }
1610
1611 return VK_SUCCESS;
1612}
1613
1614static VkSemaphore VULKAN_CreateSemaphore(VULKAN_RenderData *rendererData)
1615{
1616 VkResult result;
1617 VkSemaphore semaphore = VK_NULL_HANDLE;
1618
1619 VkSemaphoreCreateInfo semaphoreCreateInfo = { 0 };
1620 semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1621 result = vkCreateSemaphore(rendererData->device, &semaphoreCreateInfo, NULL, &semaphore);
1622 if (result != VK_SUCCESS) {
1623 SET_ERROR_CODE("vkCreateSemaphore()", result);
1624 return VK_NULL_HANDLE;
1625 }
1626 return semaphore;
1627}
1628
1629static bool VULKAN_DeviceExtensionsFound(VULKAN_RenderData *rendererData, int extensionsToCheck, const char* const* extNames)
1630{
1631 uint32_t extensionCount;
1632 bool foundExtensions = true;
1633 VkResult result = vkEnumerateDeviceExtensionProperties(rendererData->physicalDevice, NULL, &extensionCount, NULL);
1634 if (result != VK_SUCCESS ) {
1635 SET_ERROR_CODE("vkEnumerateDeviceExtensionProperties()", result);
1636 return false;
1637 }
1638 if (extensionCount > 0 ) {
1639 VkExtensionProperties *extensionProperties = (VkExtensionProperties *)SDL_calloc(extensionCount, sizeof(VkExtensionProperties));
1640 result = vkEnumerateDeviceExtensionProperties(rendererData->physicalDevice, NULL, &extensionCount, extensionProperties);
1641 if (result != VK_SUCCESS ) {
1642 SET_ERROR_CODE("vkEnumerateDeviceExtensionProperties()", result);
1643 SDL_free(extensionProperties);
1644 return false;
1645 }
1646 for (int ext = 0; ext < extensionsToCheck && foundExtensions; ext++) {
1647 bool foundExtension = false;
1648 for (uint32_t i = 0; i < extensionCount; i++) {
1649 if (SDL_strcmp(extensionProperties[i].extensionName, extNames[ext]) == 0) {
1650 foundExtension = true;
1651 break;
1652 }
1653 }
1654 foundExtensions &= foundExtension;
1655 }
1656
1657 SDL_free(extensionProperties);
1658 }
1659
1660 return foundExtensions;
1661}
1662
1663static bool VULKAN_InstanceExtensionFound(VULKAN_RenderData *rendererData, const char *extName)
1664{
1665 uint32_t extensionCount;
1666 VkResult result = vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, NULL);
1667 if (result != VK_SUCCESS ) {
1668 SET_ERROR_CODE("vkEnumerateInstanceExtensionProperties()", result);
1669 return false;
1670 }
1671 if (extensionCount > 0 ) {
1672 VkExtensionProperties *extensionProperties = (VkExtensionProperties *)SDL_calloc(extensionCount, sizeof(VkExtensionProperties));
1673 result = vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensionProperties);
1674 if (result != VK_SUCCESS ) {
1675 SET_ERROR_CODE("vkEnumerateInstanceExtensionProperties()", result);
1676 SDL_free(extensionProperties);
1677 return false;
1678 }
1679 for (uint32_t i = 0; i< extensionCount; i++) {
1680 if (SDL_strcmp(extensionProperties[i].extensionName, extName) == 0) {
1681 SDL_free(extensionProperties);
1682 return true;
1683 }
1684 }
1685 SDL_free(extensionProperties);
1686 }
1687
1688 return false;
1689}
1690
1691static bool VULKAN_ValidationLayersFound(void)
1692{
1693 uint32_t instanceLayerCount = 0;
1694 uint32_t i;
1695 bool foundValidation = false;
1696
1697 vkEnumerateInstanceLayerProperties(&instanceLayerCount, NULL);
1698 if (instanceLayerCount > 0) {
1699 VkLayerProperties *instanceLayers = (VkLayerProperties *)SDL_calloc(instanceLayerCount, sizeof(VkLayerProperties));
1700 vkEnumerateInstanceLayerProperties(&instanceLayerCount, instanceLayers);
1701 for (i = 0; i < instanceLayerCount; i++) {
1702 if (!SDL_strcmp(SDL_VULKAN_VALIDATION_LAYER_NAME, instanceLayers[i].layerName)) {
1703 foundValidation = true;
1704 break;
1705 }
1706 }
1707 SDL_free(instanceLayers);
1708 }
1709
1710 return foundValidation;
1711}
1712
1713// Create resources that depend on the device.
1714static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_PropertiesID create_props)
1715{
1716 static const char *const deviceExtensionNames[] = {
1717 VK_KHR_SWAPCHAIN_EXTENSION_NAME,
1718 /* VK_KHR_sampler_ycbcr_conversion + dependent extensions.
1719 Note VULKAN_DeviceExtensionsFound() call below, if these get moved in this
1720 array, update that check too.
1721 */
1722 VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
1723 VK_KHR_MAINTENANCE1_EXTENSION_NAME,
1724 VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
1725 VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,
1726 };
1727 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
1728 SDL_VideoDevice *device = SDL_GetVideoDevice();
1729 VkResult result = VK_SUCCESS;
1730 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
1731 bool createDebug = SDL_GetHintBoolean(SDL_HINT_RENDER_VULKAN_DEBUG, false);
1732 const char *validationLayerName[] = { SDL_VULKAN_VALIDATION_LAYER_NAME };
1733
1734 if (!SDL_Vulkan_LoadLibrary(NULL)) {
1735 SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "SDL_Vulkan_LoadLibrary failed" );
1736 return VK_ERROR_UNKNOWN;
1737 }
1738 vkGetInstanceProcAddr = device ? (PFN_vkGetInstanceProcAddr)device->vulkan_config.vkGetInstanceProcAddr : NULL;
1739 if(!vkGetInstanceProcAddr) {
1740 SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "vkGetInstanceProcAddr is NULL" );
1741 return VK_ERROR_UNKNOWN;
1742 }
1743
1744 // Load global Vulkan functions
1745 rendererData->vkGetInstanceProcAddr = vkGetInstanceProcAddr;
1746 if (!VULKAN_LoadGlobalFunctions(rendererData)) {
1747 return VK_ERROR_UNKNOWN;
1748 }
1749
1750 // Check for colorspace extension
1751 rendererData->supportsEXTSwapchainColorspace = VK_FALSE;
1752 if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR ||
1753 renderer->output_colorspace == SDL_COLORSPACE_HDR10) {
1754 rendererData->supportsEXTSwapchainColorspace = VULKAN_InstanceExtensionFound(rendererData, VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME);
1755 if (!rendererData->supportsEXTSwapchainColorspace) {
1756 SDL_SetError("Using HDR output but %s not supported", VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME);
1757 return VK_ERROR_UNKNOWN;
1758 }
1759 }
1760
1761 // Check for VK_KHR_get_physical_device_properties2
1762 rendererData->supportsKHRGetPhysicalDeviceProperties2 = VULKAN_InstanceExtensionFound(rendererData, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
1763
1764 // Create VkInstance
1765 rendererData->instance = (VkInstance)SDL_GetPointerProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER, NULL);
1766 if (rendererData->instance) {
1767 rendererData->instance_external = true;
1768 } else {
1769 VkInstanceCreateInfo instanceCreateInfo = { 0 };
1770 VkApplicationInfo appInfo = { 0 };
1771 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
1772 appInfo.apiVersion = VK_API_VERSION_1_0;
1773 instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
1774 instanceCreateInfo.pApplicationInfo = &appInfo;
1775 char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount);
1776
1777 const char **instanceExtensionsCopy = (const char **)SDL_calloc(instanceCreateInfo.enabledExtensionCount + 2, sizeof(const char *));
1778 for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) {
1779 instanceExtensionsCopy[i] = instanceExtensions[i];
1780 }
1781 if (rendererData->supportsEXTSwapchainColorspace) {
1782 instanceExtensionsCopy[instanceCreateInfo.enabledExtensionCount] = VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME;
1783 instanceCreateInfo.enabledExtensionCount++;
1784 }
1785 if (rendererData->supportsKHRGetPhysicalDeviceProperties2) {
1786 instanceExtensionsCopy[instanceCreateInfo.enabledExtensionCount] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;
1787 instanceCreateInfo.enabledExtensionCount++;
1788 }
1789 instanceCreateInfo.ppEnabledExtensionNames = (const char *const *)instanceExtensionsCopy;
1790 if (createDebug && VULKAN_ValidationLayersFound()) {
1791 instanceCreateInfo.ppEnabledLayerNames = validationLayerName;
1792 instanceCreateInfo.enabledLayerCount = 1;
1793 }
1794 result = vkCreateInstance(&instanceCreateInfo, NULL, &rendererData->instance);
1795 SDL_free((void *)instanceExtensionsCopy);
1796 if (result != VK_SUCCESS) {
1797 SET_ERROR_CODE("vkCreateInstance()", result);
1798 return result;
1799 }
1800 }
1801
1802 // Load instance Vulkan functions
1803 if (!VULKAN_LoadInstanceFunctions(rendererData)) {
1804 VULKAN_DestroyAll(renderer);
1805 return VK_ERROR_UNKNOWN;
1806 }
1807
1808 // Create Vulkan surface
1809 rendererData->surface = (VkSurfaceKHR)SDL_GetNumberProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_SURFACE_NUMBER, 0);
1810 if (rendererData->surface) {
1811 rendererData->surface_external = true;
1812 } else {
1813 if (!device->Vulkan_CreateSurface || !device->Vulkan_CreateSurface(device, renderer->window, rendererData->instance, NULL, &rendererData->surface)) {
1814 VULKAN_DestroyAll(renderer);
1815 SET_ERROR_MESSAGE("Vulkan_CreateSurface() failed");
1816 return VK_ERROR_UNKNOWN;
1817 }
1818 }
1819
1820 // Choose Vulkan physical device
1821 rendererData->physicalDevice = (VkPhysicalDevice)SDL_GetPointerProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_PHYSICAL_DEVICE_POINTER, NULL);
1822 if (rendererData->physicalDevice) {
1823 vkGetPhysicalDeviceMemoryProperties(rendererData->physicalDevice, &rendererData->physicalDeviceMemoryProperties);
1824 vkGetPhysicalDeviceFeatures(rendererData->physicalDevice, &rendererData->physicalDeviceFeatures);
1825 } else {
1826 if (VULKAN_FindPhysicalDevice(rendererData) != VK_SUCCESS) {
1827 VULKAN_DestroyAll(renderer);
1828 return VK_ERROR_UNKNOWN;
1829 }
1830 }
1831
1832 if (SDL_HasProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER)) {
1833 rendererData->graphicsQueueFamilyIndex = (uint32_t)SDL_GetNumberProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER, 0);
1834 }
1835 if (SDL_HasProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER)) {
1836 rendererData->presentQueueFamilyIndex = (uint32_t)SDL_GetNumberProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER, 0);
1837 }
1838
1839 if (rendererData->supportsKHRGetPhysicalDeviceProperties2 &&
1840 VULKAN_DeviceExtensionsFound(rendererData, 4, &deviceExtensionNames[1])) {
1841 rendererData->supportsKHRSamplerYCbCrConversion = true;
1842 }
1843
1844 // Create Vulkan device
1845 rendererData->device = (VkDevice)SDL_GetPointerProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER, NULL);
1846 if (rendererData->device) {
1847 rendererData->device_external = true;
1848 } else {
1849 VkPhysicalDeviceSamplerYcbcrConversionFeatures deviceSamplerYcbcrConversionFeatures = { 0 };
1850 VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = { { 0 }, { 0 } };
1851 static const float queuePriority[] = { 1.0f };
1852
1853 VkDeviceCreateInfo deviceCreateInfo = { 0 };
1854 deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
1855 deviceCreateInfo.queueCreateInfoCount = 0;
1856 deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
1857 deviceCreateInfo.pEnabledFeatures = NULL;
1858 deviceCreateInfo.enabledExtensionCount = (rendererData->supportsKHRSamplerYCbCrConversion) ? 5 : 1;
1859 deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames;
1860
1861 deviceQueueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
1862 deviceQueueCreateInfo[0].queueFamilyIndex = rendererData->graphicsQueueFamilyIndex;
1863 deviceQueueCreateInfo[0].queueCount = 1;
1864 deviceQueueCreateInfo[0].pQueuePriorities = queuePriority;
1865 ++deviceCreateInfo.queueCreateInfoCount;
1866
1867 if (rendererData->presentQueueFamilyIndex != rendererData->graphicsQueueFamilyIndex) {
1868 deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
1869 deviceQueueCreateInfo[1].queueFamilyIndex = rendererData->presentQueueFamilyIndex;
1870 deviceQueueCreateInfo[1].queueCount = 1;
1871 deviceQueueCreateInfo[1].pQueuePriorities = queuePriority;
1872 ++deviceCreateInfo.queueCreateInfoCount;
1873 }
1874
1875 if (rendererData->supportsKHRSamplerYCbCrConversion) {
1876 deviceSamplerYcbcrConversionFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
1877 deviceSamplerYcbcrConversionFeatures.samplerYcbcrConversion = VK_TRUE;
1878 deviceSamplerYcbcrConversionFeatures.pNext = (void *)deviceCreateInfo.pNext;
1879 deviceCreateInfo.pNext = &deviceSamplerYcbcrConversionFeatures;
1880 }
1881
1882 result = vkCreateDevice(rendererData->physicalDevice, &deviceCreateInfo, NULL, &rendererData->device);
1883 if (result != VK_SUCCESS) {
1884 SET_ERROR_CODE("vkCreateDevice()", result);
1885 VULKAN_DestroyAll(renderer);
1886 return result;
1887 }
1888 }
1889
1890 if (!VULKAN_LoadDeviceFunctions(rendererData)) {
1891 VULKAN_DestroyAll(renderer);
1892 return VK_ERROR_UNKNOWN;
1893 }
1894
1895 // Get graphics/present queues
1896 vkGetDeviceQueue(rendererData->device, rendererData->graphicsQueueFamilyIndex, 0, &rendererData->graphicsQueue);
1897 if (rendererData->graphicsQueueFamilyIndex != rendererData->presentQueueFamilyIndex) {
1898 vkGetDeviceQueue(rendererData->device, rendererData->presentQueueFamilyIndex, 0, &rendererData->presentQueue);
1899 } else {
1900 rendererData->presentQueue = rendererData->graphicsQueue;
1901 }
1902
1903 // Create command pool/command buffers
1904 VkCommandPoolCreateInfo commandPoolCreateInfo = { 0 };
1905 commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1906 commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
1907 commandPoolCreateInfo.queueFamilyIndex = rendererData->graphicsQueueFamilyIndex;
1908 result = vkCreateCommandPool(rendererData->device, &commandPoolCreateInfo, NULL, &rendererData->commandPool);
1909 if (result != VK_SUCCESS) {
1910 VULKAN_DestroyAll(renderer);
1911 SET_ERROR_CODE("vkCreateCommandPool()", result);
1912 return result;
1913 }
1914
1915 if (VULKAN_GetSurfaceFormats(rendererData) != VK_SUCCESS) {
1916 VULKAN_DestroyAll(renderer);
1917 return result;
1918 }
1919
1920 // Create shaders / layouts
1921 for (uint32_t i = 0; i < NUM_SHADERS; i++) {
1922 VULKAN_Shader shader = (VULKAN_Shader)i;
1923 VkShaderModuleCreateInfo shaderModuleCreateInfo = { 0 };
1924 shaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
1925 VULKAN_GetVertexShader(shader, &shaderModuleCreateInfo.pCode, &shaderModuleCreateInfo.codeSize);
1926 result = vkCreateShaderModule(rendererData->device, &shaderModuleCreateInfo, NULL, &rendererData->vertexShaderModules[i]);
1927 if (result != VK_SUCCESS) {
1928 VULKAN_DestroyAll(renderer);
1929 SET_ERROR_CODE("vkCreateShaderModule()", result);
1930 return result;
1931 }
1932 VULKAN_GetPixelShader(shader, &shaderModuleCreateInfo.pCode, &shaderModuleCreateInfo.codeSize);
1933 result = vkCreateShaderModule(rendererData->device, &shaderModuleCreateInfo, NULL, &rendererData->fragmentShaderModules[i]);
1934 if (result != VK_SUCCESS) {
1935 VULKAN_DestroyAll(renderer);
1936 SET_ERROR_CODE("vkCreateShaderModule()", result);
1937 return result;
1938 }
1939 }
1940
1941 // Descriptor set layout / pipeline layout
1942 result = VULKAN_CreateDescriptorSetAndPipelineLayout(rendererData, VK_NULL_HANDLE, &rendererData->descriptorSetLayout, &rendererData->pipelineLayout);
1943 if (result != VK_SUCCESS) {
1944 VULKAN_DestroyAll(renderer);
1945 return result;
1946 }
1947
1948 // Create default vertex buffers
1949 for (uint32_t i = 0; i < SDL_VULKAN_NUM_VERTEX_BUFFERS; ++i) {
1950 VULKAN_CreateVertexBuffer(rendererData, i, SDL_VULKAN_VERTEX_BUFFER_DEFAULT_SIZE);
1951 }
1952
1953 // Create samplers
1954 {
1955 static struct
1956 {
1957 VkFilter filter;
1958 VkSamplerAddressMode address;
1959 } samplerParams[] = {
1960 { VK_FILTER_NEAREST, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE },
1961 { VK_FILTER_NEAREST, VK_SAMPLER_ADDRESS_MODE_REPEAT },
1962 { VK_FILTER_LINEAR, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE },
1963 { VK_FILTER_LINEAR, VK_SAMPLER_ADDRESS_MODE_REPEAT },
1964 };
1965 SDL_COMPILE_TIME_ASSERT(samplerParams_SIZE, SDL_arraysize(samplerParams) == VULKAN_SAMPLER_COUNT);
1966 VkSamplerCreateInfo samplerCreateInfo = { 0 };
1967 samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
1968 samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
1969 samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
1970 samplerCreateInfo.mipLodBias = 0.0f;
1971 samplerCreateInfo.anisotropyEnable = VK_FALSE;
1972 samplerCreateInfo.maxAnisotropy = 1.0f;
1973 samplerCreateInfo.minLod = 0.0f;
1974 samplerCreateInfo.maxLod = 1000.0f;
1975 for (int i = 0; i < SDL_arraysize(samplerParams); ++i) {
1976 samplerCreateInfo.magFilter = samplerParams[i].filter;
1977 samplerCreateInfo.minFilter = samplerParams[i].filter;
1978 samplerCreateInfo.addressModeU = samplerParams[i].address;
1979 samplerCreateInfo.addressModeV = samplerParams[i].address;
1980 result = vkCreateSampler(rendererData->device, &samplerCreateInfo, NULL, &rendererData->samplers[i]);
1981 if (result != VK_SUCCESS) {
1982 VULKAN_DestroyAll(renderer);
1983 SET_ERROR_CODE("vkCreateSampler()", result);
1984 return result;
1985 }
1986 }
1987 }
1988
1989 SDL_PropertiesID props = SDL_GetRendererProperties(renderer);
1990 SDL_SetPointerProperty(props, SDL_PROP_RENDERER_VULKAN_INSTANCE_POINTER, rendererData->instance);
1991 SDL_SetNumberProperty(props, SDL_PROP_RENDERER_VULKAN_SURFACE_NUMBER, (Sint64)rendererData->surface);
1992 SDL_SetPointerProperty(props, SDL_PROP_RENDERER_VULKAN_PHYSICAL_DEVICE_POINTER, rendererData->physicalDevice);
1993 SDL_SetPointerProperty(props, SDL_PROP_RENDERER_VULKAN_DEVICE_POINTER, rendererData->device);
1994 SDL_SetNumberProperty(props, SDL_PROP_RENDERER_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER, rendererData->graphicsQueueFamilyIndex);
1995 SDL_SetNumberProperty(props, SDL_PROP_RENDERER_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER, rendererData->presentQueueFamilyIndex);
1996
1997 return VK_SUCCESS;
1998}
1999
2000static VkResult VULKAN_CreateFramebuffersAndRenderPasses(SDL_Renderer *renderer, int w, int h,
2001 VkFormat format, int imageViewCount, VkImageView *imageViews, VkFramebuffer *framebuffers, VkRenderPass renderPasses[VULKAN_RENDERPASS_COUNT])
2002{
2003 VULKAN_RenderData *rendererData = (VULKAN_RenderData *) renderer->internal;
2004 VkResult result;
2005
2006 VkAttachmentDescription attachmentDescription = { 0 };
2007 attachmentDescription.format = format;
2008 attachmentDescription.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
2009 attachmentDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
2010 attachmentDescription.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
2011 attachmentDescription.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
2012 attachmentDescription.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
2013 attachmentDescription.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
2014 attachmentDescription.samples = VK_SAMPLE_COUNT_1_BIT;
2015 attachmentDescription.flags = 0;
2016
2017 VkAttachmentReference colorAttachmentReference = { 0 };
2018 colorAttachmentReference.attachment = 0;
2019 colorAttachmentReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
2020
2021 VkSubpassDescription subpassDescription = { 0 };
2022 subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
2023 subpassDescription.flags = 0;
2024 subpassDescription.inputAttachmentCount = 0;
2025 subpassDescription.pInputAttachments = NULL;
2026 subpassDescription.colorAttachmentCount = 1;
2027 subpassDescription.pColorAttachments = &colorAttachmentReference;
2028 subpassDescription.pResolveAttachments = NULL;
2029 subpassDescription.pDepthStencilAttachment = NULL;
2030 subpassDescription.preserveAttachmentCount = 0;
2031 subpassDescription.pPreserveAttachments = NULL;
2032
2033 VkSubpassDependency subPassDependency = { 0 };
2034 subPassDependency.srcSubpass = VK_SUBPASS_EXTERNAL;
2035 subPassDependency.dstSubpass = 0;
2036 subPassDependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
2037 subPassDependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
2038 subPassDependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
2039 subPassDependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
2040 subPassDependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
2041
2042 VkRenderPassCreateInfo renderPassCreateInfo = { 0 };
2043 renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
2044 renderPassCreateInfo.flags = 0;
2045 renderPassCreateInfo.attachmentCount = 1;
2046 renderPassCreateInfo.pAttachments = &attachmentDescription;
2047 renderPassCreateInfo.subpassCount = 1;
2048 renderPassCreateInfo.pSubpasses = &subpassDescription;
2049 renderPassCreateInfo.dependencyCount = 1;
2050 renderPassCreateInfo.pDependencies = &subPassDependency;
2051
2052 result = vkCreateRenderPass(rendererData->device, &renderPassCreateInfo, NULL, &renderPasses[VULKAN_RENDERPASS_LOAD]);
2053 if (result != VK_SUCCESS) {
2054 SET_ERROR_CODE("vkCreateRenderPass()", result);
2055 return result;
2056 }
2057
2058 attachmentDescription.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
2059 result = vkCreateRenderPass(rendererData->device, &renderPassCreateInfo, NULL, &renderPasses[VULKAN_RENDERPASS_CLEAR]);
2060 if (result != VK_SUCCESS) {
2061 SET_ERROR_CODE("vkCreateRenderPass()", result);
2062 return result;
2063 }
2064
2065 VkFramebufferCreateInfo framebufferCreateInfo = { 0 };
2066 framebufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
2067 framebufferCreateInfo.pNext = NULL;
2068 framebufferCreateInfo.renderPass = rendererData->renderPasses[VULKAN_RENDERPASS_LOAD];
2069 framebufferCreateInfo.attachmentCount = 1;
2070 framebufferCreateInfo.width = w;
2071 framebufferCreateInfo.height = h;
2072 framebufferCreateInfo.layers = 1;
2073
2074 for (int i = 0; i < imageViewCount; i++) {
2075 framebufferCreateInfo.pAttachments = &imageViews[i];
2076 result = vkCreateFramebuffer(rendererData->device, &framebufferCreateInfo, NULL, &framebuffers[i]);
2077 if (result != VK_SUCCESS) {
2078 SET_ERROR_CODE("vkCreateFramebuffer()", result);
2079 return result;
2080 }
2081 }
2082
2083 return result;
2084}
2085
2086static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h)
2087{
2088 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
2089 VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(rendererData->physicalDevice, rendererData->surface, &rendererData->surfaceCapabilities);
2090 if (result != VK_SUCCESS) {
2091 SET_ERROR_CODE("vkGetPhysicalDeviceSurfaceCapabilitiesKHR()", result);
2092 return result;
2093 }
2094
2095 // clean up previous swapchain resources
2096 if (rendererData->swapchainImageViews) {
2097 for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
2098 vkDestroyImageView(rendererData->device, rendererData->swapchainImageViews[i], NULL);
2099 }
2100 SDL_free(rendererData->swapchainImageViews);
2101 rendererData->swapchainImageViews = NULL;
2102 }
2103 if (rendererData->fences) {
2104 for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
2105 if (rendererData->fences[i] != VK_NULL_HANDLE) {
2106 vkDestroyFence(rendererData->device, rendererData->fences[i], NULL);
2107 }
2108 }
2109 SDL_free(rendererData->fences);
2110 rendererData->fences = NULL;
2111 }
2112 if (rendererData->commandBuffers) {
2113 vkResetCommandPool(rendererData->device, rendererData->commandPool, 0);
2114 SDL_free(rendererData->commandBuffers);
2115 rendererData->commandBuffers = NULL;
2116 rendererData->currentCommandBuffer = VK_NULL_HANDLE;
2117 rendererData->currentCommandBufferIndex = 0;
2118 }
2119 if (rendererData->framebuffers) {
2120 for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
2121 if (rendererData->framebuffers[i] != VK_NULL_HANDLE) {
2122 vkDestroyFramebuffer(rendererData->device, rendererData->framebuffers[i], NULL);
2123 }
2124 }
2125 SDL_free(rendererData->framebuffers);
2126 rendererData->framebuffers = NULL;
2127 }
2128 if (rendererData->descriptorPools) {
2129 SDL_assert(rendererData->numDescriptorPools);
2130 for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
2131 for (uint32_t j = 0; j < rendererData->numDescriptorPools[i]; j++) {
2132 if (rendererData->descriptorPools[i][j] != VK_NULL_HANDLE) {
2133 vkDestroyDescriptorPool(rendererData->device, rendererData->descriptorPools[i][j], NULL);
2134 }
2135 }
2136 SDL_free(rendererData->descriptorPools[i]);
2137 }
2138 SDL_free(rendererData->descriptorPools);
2139 rendererData->descriptorPools = NULL;
2140 SDL_free(rendererData->numDescriptorPools);
2141 rendererData->numDescriptorPools = NULL;
2142 }
2143 if (rendererData->imageAvailableSemaphores) {
2144 for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) {
2145 if (rendererData->imageAvailableSemaphores[i] != VK_NULL_HANDLE) {
2146 vkDestroySemaphore(rendererData->device, rendererData->imageAvailableSemaphores[i], NULL);
2147 }
2148 }
2149 SDL_free(rendererData->imageAvailableSemaphores);
2150 rendererData->imageAvailableSemaphores = NULL;
2151 }
2152 if (rendererData->renderingFinishedSemaphores) {
2153 for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) {
2154 if (rendererData->renderingFinishedSemaphores[i] != VK_NULL_HANDLE) {
2155 vkDestroySemaphore(rendererData->device, rendererData->renderingFinishedSemaphores[i], NULL);
2156 }
2157 }
2158 SDL_free(rendererData->renderingFinishedSemaphores);
2159 rendererData->renderingFinishedSemaphores = NULL;
2160 }
2161 if (rendererData->uploadBuffers) {
2162 for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
2163 for (uint32_t j = 0; j < SDL_VULKAN_NUM_UPLOAD_BUFFERS; j++) {
2164 VULKAN_DestroyBuffer(rendererData, &rendererData->uploadBuffers[i][j]);
2165 }
2166 SDL_free(rendererData->uploadBuffers[i]);
2167 }
2168 SDL_free(rendererData->uploadBuffers);
2169 rendererData->uploadBuffers = NULL;
2170 }
2171 if (rendererData->constantBuffers) {
2172 SDL_assert(rendererData->numConstantBuffers);
2173 for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) {
2174 for (uint32_t j = 0; j < rendererData->numConstantBuffers[i]; j++) {
2175 VULKAN_DestroyBuffer(rendererData, &rendererData->constantBuffers[i][j]);
2176 }
2177 SDL_free(rendererData->constantBuffers[i]);
2178 }
2179 SDL_free(rendererData->constantBuffers);
2180 rendererData->constantBuffers = NULL;
2181 SDL_free(rendererData->numConstantBuffers);
2182 rendererData->numConstantBuffers = NULL;
2183 }
2184
2185 // pick an image count
2186 rendererData->swapchainDesiredImageCount = rendererData->surfaceCapabilities.minImageCount + SDL_VULKAN_FRAME_QUEUE_DEPTH;
2187 if ((rendererData->swapchainDesiredImageCount > rendererData->surfaceCapabilities.maxImageCount) &&
2188 (rendererData->surfaceCapabilities.maxImageCount > 0)) {
2189 rendererData->swapchainDesiredImageCount = rendererData->surfaceCapabilities.maxImageCount;
2190 }
2191
2192 VkFormat desiredFormat = VK_FORMAT_B8G8R8A8_UNORM;
2193 VkColorSpaceKHR desiredColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
2194 if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
2195 desiredFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
2196 desiredColorSpace = VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT;
2197 }
2198 else if (renderer->output_colorspace == SDL_COLORSPACE_HDR10) {
2199 desiredFormat = VK_FORMAT_A2B10G10R10_UNORM_PACK32;
2200 desiredColorSpace = VK_COLOR_SPACE_HDR10_ST2084_EXT;
2201 }
2202
2203 if ((rendererData->surfaceFormatsCount == 1) &&
2204 (rendererData->surfaceFormats[0].format == VK_FORMAT_UNDEFINED)) {
2205 // aren't any preferred formats, so we pick
2206 rendererData->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
2207 rendererData->surfaceFormat.format = desiredFormat;
2208 } else {
2209 rendererData->surfaceFormat = rendererData->surfaceFormats[0];
2210 rendererData->surfaceFormat.colorSpace = rendererData->surfaceFormats[0].colorSpace;
2211 for (uint32_t i = 0; i < rendererData->surfaceFormatsCount; i++) {
2212 if (rendererData->surfaceFormats[i].format == desiredFormat &&
2213 rendererData->surfaceFormats[i].colorSpace == desiredColorSpace) {
2214 rendererData->surfaceFormat.colorSpace = rendererData->surfaceFormats[i].colorSpace;
2215 rendererData->surfaceFormat = rendererData->surfaceFormats[i];
2216 break;
2217 }
2218 }
2219 }
2220
2221 rendererData->swapchainSize.width = SDL_clamp((uint32_t)w,
2222 rendererData->surfaceCapabilities.minImageExtent.width,
2223 rendererData->surfaceCapabilities.maxImageExtent.width);
2224
2225 rendererData->swapchainSize.height = SDL_clamp((uint32_t)h,
2226 rendererData->surfaceCapabilities.minImageExtent.height,
2227 rendererData->surfaceCapabilities.maxImageExtent.height);
2228
2229 // Handle rotation
2230 rendererData->swapChainPreTransform = rendererData->surfaceCapabilities.currentTransform;
2231 if (rendererData->swapChainPreTransform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
2232 rendererData->swapChainPreTransform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) {
2233 uint32_t tempWidth = rendererData->swapchainSize.width;
2234 rendererData->swapchainSize.width = rendererData->swapchainSize.height;
2235 rendererData->swapchainSize.height = tempWidth;
2236 }
2237
2238 if (rendererData->swapchainSize.width == 0 && rendererData->swapchainSize.height == 0) {
2239 // Don't recreate the swapchain if size is (0,0), just fail and continue attempting creation
2240 return VK_ERROR_OUT_OF_DATE_KHR;
2241 }
2242
2243 // Choose a present mode. If vsync is requested, then use VK_PRESENT_MODE_FIFO_KHR which is guaranteed to be supported
2244 VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
2245 if (rendererData->vsync <= 0) {
2246 uint32_t presentModeCount = 0;
2247 result = vkGetPhysicalDeviceSurfacePresentModesKHR(rendererData->physicalDevice, rendererData->surface, &presentModeCount, NULL);
2248 if (result != VK_SUCCESS) {
2249 SET_ERROR_CODE("vkGetPhysicalDeviceSurfacePresentModesKHR()", result);
2250 return result;
2251 }
2252 if (presentModeCount > 0) {
2253 VkPresentModeKHR *presentModes = (VkPresentModeKHR *)SDL_calloc(presentModeCount, sizeof(VkPresentModeKHR));
2254 result = vkGetPhysicalDeviceSurfacePresentModesKHR(rendererData->physicalDevice, rendererData->surface, &presentModeCount, presentModes);
2255 if (result != VK_SUCCESS) {
2256 SET_ERROR_CODE("vkGetPhysicalDeviceSurfacePresentModesKHR()", result);
2257 SDL_free(presentModes);
2258 return result;
2259 }
2260
2261 if (rendererData->vsync == 0) {
2262 /* If vsync is not requested, in favor these options in order:
2263 VK_PRESENT_MODE_IMMEDIATE_KHR - no v-sync with tearing
2264 VK_PRESENT_MODE_MAILBOX_KHR - no v-sync without tearing
2265 VK_PRESENT_MODE_FIFO_RELAXED_KHR - no v-sync, may tear */
2266 for (uint32_t i = 0; i < presentModeCount; i++) {
2267 if (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR) {
2268 presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
2269 break;
2270 }
2271 else if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
2272 presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
2273 }
2274 else if ((presentMode != VK_PRESENT_MODE_MAILBOX_KHR) &&
2275 (presentModes[i] == VK_PRESENT_MODE_FIFO_RELAXED_KHR)) {
2276 presentMode = VK_PRESENT_MODE_FIFO_RELAXED_KHR;
2277 }
2278 }
2279 } else if (rendererData->vsync == -1) {
2280 for (uint32_t i = 0; i < presentModeCount; i++) {
2281 if (presentModes[i] == VK_PRESENT_MODE_FIFO_RELAXED_KHR) {
2282 presentMode = VK_PRESENT_MODE_FIFO_RELAXED_KHR;
2283 break;
2284 }
2285 }
2286 }
2287 SDL_free(presentModes);
2288 }
2289 }
2290
2291 VkSwapchainCreateInfoKHR swapchainCreateInfo = { 0 };
2292 swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
2293 swapchainCreateInfo.surface = rendererData->surface;
2294 swapchainCreateInfo.minImageCount = rendererData->swapchainDesiredImageCount;
2295 swapchainCreateInfo.imageFormat = rendererData->surfaceFormat.format;
2296 swapchainCreateInfo.imageColorSpace = rendererData->surfaceFormat.colorSpace;
2297 swapchainCreateInfo.imageExtent = rendererData->swapchainSize;
2298 swapchainCreateInfo.imageArrayLayers = 1;
2299 swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
2300 swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
2301 swapchainCreateInfo.preTransform = rendererData->swapChainPreTransform;
2302 swapchainCreateInfo.compositeAlpha = (renderer->window->flags & SDL_WINDOW_TRANSPARENT) ? (VkCompositeAlphaFlagBitsKHR)0 : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
2303 swapchainCreateInfo.presentMode = presentMode;
2304 swapchainCreateInfo.clipped = VK_TRUE;
2305 swapchainCreateInfo.oldSwapchain = rendererData->swapchain;
2306 result = vkCreateSwapchainKHR(rendererData->device, &swapchainCreateInfo, NULL, &rendererData->swapchain);
2307
2308 if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
2309 vkDestroySwapchainKHR(rendererData->device, swapchainCreateInfo.oldSwapchain, NULL);
2310 }
2311
2312 if (result != VK_SUCCESS) {
2313 rendererData->swapchain = VK_NULL_HANDLE;
2314 SET_ERROR_CODE("vkCreateSwapchainKHR()", result);
2315 return result;
2316 }
2317
2318 SDL_free(rendererData->swapchainImages);
2319 rendererData->swapchainImages = NULL;
2320 result = vkGetSwapchainImagesKHR(rendererData->device, rendererData->swapchain, &rendererData->swapchainImageCount, NULL);
2321 if (result != VK_SUCCESS) {
2322 rendererData->swapchainImageCount = 0;
2323 SET_ERROR_CODE("vkGetSwapchainImagesKHR()", result);
2324 return result;
2325 }
2326
2327 rendererData->swapchainImages = (VkImage *)SDL_malloc(sizeof(VkImage) * rendererData->swapchainImageCount);
2328 result = vkGetSwapchainImagesKHR(rendererData->device,
2329 rendererData->swapchain,
2330 &rendererData->swapchainImageCount,
2331 rendererData->swapchainImages);
2332 if (result != VK_SUCCESS) {
2333 SDL_free(rendererData->swapchainImages);
2334 rendererData->swapchainImages = NULL;
2335 rendererData->swapchainImageCount = 0;
2336 SET_ERROR_CODE("vkGetSwapchainImagesKHR()", result);
2337 return result;
2338 }
2339
2340 // Create VkImageView's for swapchain images
2341 {
2342 VkImageViewCreateInfo imageViewCreateInfo = { 0 };
2343 imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
2344 imageViewCreateInfo.flags = 0;
2345 imageViewCreateInfo.format = rendererData->surfaceFormat.format;
2346 imageViewCreateInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
2347 imageViewCreateInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
2348 imageViewCreateInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
2349 imageViewCreateInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
2350 imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2351 imageViewCreateInfo.subresourceRange.baseArrayLayer = 0;
2352 imageViewCreateInfo.subresourceRange.baseMipLevel = 0;
2353 imageViewCreateInfo.subresourceRange.layerCount = 1;
2354 imageViewCreateInfo.subresourceRange.levelCount = 1;
2355 imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
2356 rendererData->swapchainImageViews = (VkImageView *)SDL_calloc(rendererData->swapchainImageCount, sizeof(VkImageView));
2357 SDL_free(rendererData->swapchainImageLayouts);
2358 rendererData->swapchainImageLayouts = (VkImageLayout *)SDL_calloc(rendererData->swapchainImageCount, sizeof(VkImageLayout));
2359 for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
2360 imageViewCreateInfo.image = rendererData->swapchainImages[i];
2361 result = vkCreateImageView(rendererData->device, &imageViewCreateInfo, NULL, &rendererData->swapchainImageViews[i]);
2362 if (result != VK_SUCCESS) {
2363 VULKAN_DestroyAll(renderer);
2364 SET_ERROR_CODE("vkCreateImageView()", result);
2365 return result;
2366 }
2367 rendererData->swapchainImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED;
2368 }
2369
2370 }
2371
2372 VkCommandBufferAllocateInfo commandBufferAllocateInfo = { 0 };
2373 commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
2374 commandBufferAllocateInfo.commandPool = rendererData->commandPool;
2375 commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
2376 commandBufferAllocateInfo.commandBufferCount = rendererData->swapchainImageCount;
2377 rendererData->commandBuffers = (VkCommandBuffer *)SDL_calloc(rendererData->swapchainImageCount, sizeof(VkCommandBuffer));
2378 result = vkAllocateCommandBuffers(rendererData->device, &commandBufferAllocateInfo, rendererData->commandBuffers);
2379 if (result != VK_SUCCESS) {
2380 VULKAN_DestroyAll(renderer);
2381 SET_ERROR_CODE("vkAllocateCommandBuffers()", result);
2382 return result;
2383 }
2384
2385 // Create fences
2386 rendererData->fences = (VkFence *)SDL_calloc(rendererData->swapchainImageCount, sizeof(VkFence));
2387 for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
2388 VkFenceCreateInfo fenceCreateInfo = { 0 };
2389 fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
2390 fenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
2391 result = vkCreateFence(rendererData->device, &fenceCreateInfo, NULL, &rendererData->fences[i]);
2392 if (result != VK_SUCCESS) {
2393 VULKAN_DestroyAll(renderer);
2394 SET_ERROR_CODE("vkCreateFence()", result);
2395 return result;
2396 }
2397 }
2398
2399 // Create renderpasses and framebuffer
2400 for (uint32_t i = 0; i < SDL_arraysize(rendererData->renderPasses); i++) {
2401 if (rendererData->renderPasses[i] != VK_NULL_HANDLE) {
2402 vkDestroyRenderPass(rendererData->device, rendererData->renderPasses[i], NULL);
2403 rendererData->renderPasses[i] = VK_NULL_HANDLE;
2404 }
2405 }
2406 rendererData->framebuffers = (VkFramebuffer *)SDL_calloc(rendererData->swapchainImageCount, sizeof(VkFramebuffer));
2407 result = VULKAN_CreateFramebuffersAndRenderPasses(renderer,
2408 rendererData->swapchainSize.width,
2409 rendererData->swapchainSize.height,
2410 rendererData->surfaceFormat.format,
2411 rendererData->swapchainImageCount,
2412 rendererData->swapchainImageViews,
2413 rendererData->framebuffers,
2414 rendererData->renderPasses);
2415 if (result != VK_SUCCESS) {
2416 VULKAN_DestroyAll(renderer);
2417 SET_ERROR_CODE("VULKAN_CreateFramebuffersAndRenderPasses()", result);
2418 return result;
2419 }
2420
2421 // Create descriptor pools - start by allocating one per swapchain image, let it grow if more are needed
2422 rendererData->descriptorPools = (VkDescriptorPool **)SDL_calloc(rendererData->swapchainImageCount, sizeof(VkDescriptorPool*));
2423 rendererData->numDescriptorPools = (uint32_t *)SDL_calloc(rendererData->swapchainImageCount, sizeof(uint32_t));
2424 for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
2425 // Start by just allocating one pool, it will grow if needed
2426 rendererData->numDescriptorPools[i] = 1;
2427 rendererData->descriptorPools[i] = (VkDescriptorPool *)SDL_calloc(1, sizeof(VkDescriptorPool));
2428 rendererData->descriptorPools[i][0] = VULKAN_AllocateDescriptorPool(rendererData);
2429 if (result != VK_SUCCESS) {
2430 VULKAN_DestroyAll(renderer);
2431 return result;
2432 }
2433 }
2434
2435 // Create semaphores
2436 rendererData->imageAvailableSemaphores = (VkSemaphore *)SDL_calloc(rendererData->swapchainImageCount, sizeof(VkSemaphore));
2437 rendererData->renderingFinishedSemaphores = (VkSemaphore *)SDL_calloc(rendererData->swapchainImageCount, sizeof(VkSemaphore));
2438 for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
2439 rendererData->imageAvailableSemaphores[i] = VULKAN_CreateSemaphore(rendererData);
2440 if (rendererData->imageAvailableSemaphores[i] == VK_NULL_HANDLE) {
2441 VULKAN_DestroyAll(renderer);
2442 return VK_ERROR_UNKNOWN;
2443 }
2444 rendererData->renderingFinishedSemaphores[i] = VULKAN_CreateSemaphore(rendererData);
2445 if (rendererData->renderingFinishedSemaphores[i] == VK_NULL_HANDLE) {
2446 VULKAN_DestroyAll(renderer);
2447 return VK_ERROR_UNKNOWN;
2448 }
2449 }
2450
2451 // Upload buffers
2452 rendererData->uploadBuffers = (VULKAN_Buffer **)SDL_calloc(rendererData->swapchainImageCount, sizeof(VULKAN_Buffer*));
2453 for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
2454 rendererData->uploadBuffers[i] = (VULKAN_Buffer *)SDL_calloc(SDL_VULKAN_NUM_UPLOAD_BUFFERS, sizeof(VULKAN_Buffer));
2455 }
2456 SDL_free(rendererData->currentUploadBuffer);
2457 rendererData->currentUploadBuffer = (int *)SDL_calloc(rendererData->swapchainImageCount, sizeof(int));
2458
2459 // Constant buffers
2460 rendererData->constantBuffers = (VULKAN_Buffer **)SDL_calloc(rendererData->swapchainImageCount, sizeof(VULKAN_Buffer*));
2461 rendererData->numConstantBuffers = (uint32_t *)SDL_calloc(rendererData->swapchainImageCount, sizeof(uint32_t));
2462 for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) {
2463 // Start with just allocating one, will grow if needed
2464 rendererData->numConstantBuffers[i] = 1;
2465 rendererData->constantBuffers[i] = (VULKAN_Buffer *)SDL_calloc(1, sizeof(VULKAN_Buffer));
2466 result = VULKAN_AllocateBuffer(rendererData,
2467 SDL_VULKAN_CONSTANT_BUFFER_DEFAULT_SIZE,
2468 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
2469 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
2470 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
2471 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
2472 &rendererData->constantBuffers[i][0]);
2473 if (result != VK_SUCCESS) {
2474 VULKAN_DestroyAll(renderer);
2475 return result;
2476 }
2477 }
2478 rendererData->currentConstantBufferOffset = -1;
2479 rendererData->currentConstantBufferIndex = 0;
2480
2481 VULKAN_AcquireNextSwapchainImage(renderer);
2482
2483 SDL_PropertiesID props = SDL_GetRendererProperties(renderer);
2484 SDL_SetNumberProperty(props, SDL_PROP_RENDERER_VULKAN_SWAPCHAIN_IMAGE_COUNT_NUMBER, rendererData->swapchainImageCount);
2485
2486 return result;
2487}
2488
2489// Initialize all resources that change when the window's size changes.
2490static VkResult VULKAN_CreateWindowSizeDependentResources(SDL_Renderer *renderer)
2491{
2492 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
2493 VkResult result = VK_SUCCESS;
2494 int w, h;
2495
2496 // Release resources in the current command list
2497 VULKAN_IssueBatch(rendererData);
2498 VULKAN_WaitForGPU(rendererData);
2499
2500 /* The width and height of the swap chain must be based on the display's
2501 * non-rotated size.
2502 */
2503 SDL_GetWindowSizeInPixels(renderer->window, &w, &h);
2504
2505 result = VULKAN_CreateSwapChain(renderer, w, h);
2506 if (result != VK_SUCCESS) {
2507 rendererData->recreateSwapchain = VK_TRUE;
2508 }
2509
2510 rendererData->viewportDirty = true;
2511
2512 return result;
2513}
2514
2515static bool VULKAN_HandleDeviceLost(SDL_Renderer *renderer)
2516{
2517 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
2518 bool recovered = false;
2519
2520 VULKAN_DestroyAll(renderer);
2521
2522 if (VULKAN_CreateDeviceResources(renderer, rendererData->create_props) == VK_SUCCESS &&
2523 VULKAN_CreateWindowSizeDependentResources(renderer) == VK_SUCCESS) {
2524 recovered = true;
2525 } else {
2526 SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Renderer couldn't recover from device lost: %s", SDL_GetError());
2527 VULKAN_DestroyAll(renderer);
2528 }
2529
2530 // Let the application know that the device has been reset or lost
2531 SDL_Event event;
2532 SDL_zero(event);
2533 event.type = recovered ? SDL_EVENT_RENDER_DEVICE_RESET : SDL_EVENT_RENDER_DEVICE_LOST;
2534 event.render.windowID = SDL_GetWindowID(SDL_GetRenderWindow(renderer));
2535 SDL_PushEvent(&event);
2536
2537 return recovered;
2538}
2539
2540// This method is called when the window's size changes.
2541static VkResult VULKAN_UpdateForWindowSizeChange(SDL_Renderer *renderer)
2542{
2543 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
2544 // If the GPU has previous work, wait for it to be done first
2545 VULKAN_WaitForGPU(rendererData);
2546
2547 return VULKAN_CreateWindowSizeDependentResources(renderer);
2548}
2549
2550static void VULKAN_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event)
2551{
2552 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
2553
2554 if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
2555 rendererData->recreateSwapchain = true;
2556 }
2557}
2558
2559static bool VULKAN_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode)
2560{
2561 SDL_BlendFactor srcColorFactor = SDL_GetBlendModeSrcColorFactor(blendMode);
2562 SDL_BlendFactor srcAlphaFactor = SDL_GetBlendModeSrcAlphaFactor(blendMode);
2563 SDL_BlendOperation colorOperation = SDL_GetBlendModeColorOperation(blendMode);
2564 SDL_BlendFactor dstColorFactor = SDL_GetBlendModeDstColorFactor(blendMode);
2565 SDL_BlendFactor dstAlphaFactor = SDL_GetBlendModeDstAlphaFactor(blendMode);
2566 SDL_BlendOperation alphaOperation = SDL_GetBlendModeAlphaOperation(blendMode);
2567
2568 if (GetBlendFactor(srcColorFactor) == VK_BLEND_FACTOR_MAX_ENUM ||
2569 GetBlendFactor(srcAlphaFactor) == VK_BLEND_FACTOR_MAX_ENUM ||
2570 GetBlendOp(colorOperation) == VK_BLEND_OP_MAX_ENUM ||
2571 GetBlendFactor(dstColorFactor) == VK_BLEND_FACTOR_MAX_ENUM ||
2572 GetBlendFactor(dstAlphaFactor) == VK_BLEND_FACTOR_MAX_ENUM ||
2573 GetBlendOp(alphaOperation) == VK_BLEND_OP_MAX_ENUM) {
2574 return false;
2575 }
2576 return true;
2577}
2578
2579static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
2580{
2581 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
2582 VULKAN_TextureData *textureData;
2583 VkResult result;
2584 VkFormat textureFormat = SDLPixelFormatToVkTextureFormat(texture->format, renderer->output_colorspace);
2585 uint32_t width = texture->w;
2586 uint32_t height = texture->h;
2587 VkComponentMapping imageViewSwizzle = rendererData->identitySwizzle;
2588
2589 if (!rendererData->device) {
2590 return SDL_SetError("Device lost and couldn't be recovered");
2591 }
2592
2593 if (textureFormat == VK_FORMAT_UNDEFINED) {
2594 return SDL_SetError("%s, An unsupported SDL pixel format (0x%x) was specified", __FUNCTION__, texture->format);
2595 }
2596
2597 textureData = (VULKAN_TextureData *)SDL_calloc(1, sizeof(*textureData));
2598 if (!textureData) {
2599 return false;
2600 }
2601 texture->internal = textureData;
2602 if (SDL_COLORSPACETRANSFER(texture->colorspace) == SDL_TRANSFER_CHARACTERISTICS_SRGB) {
2603 textureData->shader = SHADER_RGB;
2604 } else {
2605 textureData->shader = SHADER_ADVANCED;
2606 }
2607
2608#ifdef SDL_HAVE_YUV
2609 // YUV textures must have even width and height. Also create Ycbcr conversion
2610 if (texture->format == SDL_PIXELFORMAT_YV12 ||
2611 texture->format == SDL_PIXELFORMAT_IYUV ||
2612 texture->format == SDL_PIXELFORMAT_NV12 ||
2613 texture->format == SDL_PIXELFORMAT_NV21 ||
2614 texture->format == SDL_PIXELFORMAT_P010) {
2615 const uint32_t YUV_SD_THRESHOLD = 576;
2616
2617 // Check that we have VK_KHR_sampler_ycbcr_conversion support
2618 if (!rendererData->supportsKHRSamplerYCbCrConversion) {
2619 return SDL_SetError("YUV textures require a Vulkan device that supports VK_KHR_sampler_ycbcr_conversion");
2620 }
2621
2622 VkSamplerYcbcrConversionCreateInfoKHR samplerYcbcrConversionCreateInfo = { 0 };
2623 samplerYcbcrConversionCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR;
2624
2625 // Pad width/height to multiple of 2
2626 width = (width + 1) & ~1;
2627 height = (height + 1) & ~1;
2628
2629 // Create samplerYcbcrConversion which will be used on the VkImageView and VkSampler
2630 samplerYcbcrConversionCreateInfo.format = textureFormat;
2631 switch (SDL_COLORSPACEMATRIX(texture->colorspace)) {
2632 case SDL_MATRIX_COEFFICIENTS_BT470BG:
2633 case SDL_MATRIX_COEFFICIENTS_BT601:
2634 samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR;
2635 break;
2636 case SDL_MATRIX_COEFFICIENTS_BT709:
2637 samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR;
2638 break;
2639 case SDL_MATRIX_COEFFICIENTS_BT2020_NCL:
2640 samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR;
2641 break;
2642 case SDL_MATRIX_COEFFICIENTS_UNSPECIFIED:
2643 if (texture->format == SDL_PIXELFORMAT_P010) {
2644 samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR;
2645 } else if (height > YUV_SD_THRESHOLD) {
2646 samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR;
2647 } else {
2648 samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR;
2649 }
2650 break;
2651 default:
2652 return SDL_SetError("Unsupported Ycbcr colorspace: %d", SDL_COLORSPACEMATRIX(texture->colorspace));
2653 }
2654 samplerYcbcrConversionCreateInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
2655 samplerYcbcrConversionCreateInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
2656 samplerYcbcrConversionCreateInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
2657 samplerYcbcrConversionCreateInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
2658 if (texture->format == SDL_PIXELFORMAT_YV12 ||
2659 texture->format == SDL_PIXELFORMAT_NV21) {
2660 samplerYcbcrConversionCreateInfo.components.r = VK_COMPONENT_SWIZZLE_B;
2661 samplerYcbcrConversionCreateInfo.components.b = VK_COMPONENT_SWIZZLE_R;
2662 }
2663
2664 switch (SDL_COLORSPACERANGE(texture->colorspace)) {
2665 case SDL_COLOR_RANGE_LIMITED:
2666 samplerYcbcrConversionCreateInfo.ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR;
2667 break;
2668 case SDL_COLOR_RANGE_FULL:
2669 default:
2670 samplerYcbcrConversionCreateInfo.ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR;
2671 break;
2672 }
2673
2674 switch (SDL_COLORSPACECHROMA(texture->colorspace)) {
2675 case SDL_CHROMA_LOCATION_LEFT:
2676 samplerYcbcrConversionCreateInfo.xChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN_KHR;
2677 samplerYcbcrConversionCreateInfo.yChromaOffset = VK_CHROMA_LOCATION_MIDPOINT_KHR;
2678 break;
2679 case SDL_CHROMA_LOCATION_TOPLEFT:
2680 samplerYcbcrConversionCreateInfo.xChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN_KHR;
2681 samplerYcbcrConversionCreateInfo.yChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN_KHR;
2682 break;
2683 case SDL_CHROMA_LOCATION_NONE:
2684 case SDL_CHROMA_LOCATION_CENTER:
2685 default:
2686 samplerYcbcrConversionCreateInfo.xChromaOffset = VK_CHROMA_LOCATION_MIDPOINT_KHR;
2687 samplerYcbcrConversionCreateInfo.yChromaOffset = VK_CHROMA_LOCATION_MIDPOINT_KHR;
2688 break;
2689 }
2690 samplerYcbcrConversionCreateInfo.chromaFilter = VK_FILTER_LINEAR;
2691 samplerYcbcrConversionCreateInfo.forceExplicitReconstruction = VK_FALSE;
2692
2693 result = vkCreateSamplerYcbcrConversionKHR(rendererData->device, &samplerYcbcrConversionCreateInfo, NULL, &textureData->samplerYcbcrConversion);
2694 if (result != VK_SUCCESS) {
2695 SET_ERROR_CODE("vkCreateSamplerYcbcrConversionKHR()", result);
2696 return false;
2697 }
2698
2699 // Also create VkSampler object which we will need to pass to the PSO as an immutable sampler
2700 VkSamplerCreateInfo samplerCreateInfo = { 0 };
2701 samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
2702 samplerCreateInfo.magFilter = VK_FILTER_NEAREST;
2703 samplerCreateInfo.minFilter = VK_FILTER_NEAREST;
2704 samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
2705 samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
2706 samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
2707 samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
2708 samplerCreateInfo.mipLodBias = 0.0f;
2709 samplerCreateInfo.anisotropyEnable = VK_FALSE;
2710 samplerCreateInfo.maxAnisotropy = 1.0f;
2711 samplerCreateInfo.minLod = 0.0f;
2712 samplerCreateInfo.maxLod = 1000.0f;
2713
2714 VkSamplerYcbcrConversionInfoKHR samplerYcbcrConversionInfo = { 0 };
2715 samplerYcbcrConversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR;
2716 samplerYcbcrConversionInfo.conversion = textureData->samplerYcbcrConversion;
2717 samplerCreateInfo.pNext = &samplerYcbcrConversionInfo;
2718 result = vkCreateSampler(rendererData->device, &samplerCreateInfo, NULL, &textureData->samplerYcbcr);
2719 if (result != VK_SUCCESS) {
2720 SET_ERROR_CODE("vkCreateSampler()", result);
2721 return false;
2722 }
2723
2724 // Allocate special descriptor set layout with samplerYcbcr baked as an immutable sampler
2725 result = VULKAN_CreateDescriptorSetAndPipelineLayout(rendererData, textureData->samplerYcbcr, &textureData->descriptorSetLayoutYcbcr, &textureData->pipelineLayoutYcbcr);
2726 if (result != VK_SUCCESS) {
2727 return false;
2728 }
2729 }
2730#endif
2731 textureData->width = width;
2732 textureData->height = height;
2733
2734 VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
2735 if (texture->access == SDL_TEXTUREACCESS_TARGET) {
2736 usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
2737 }
2738
2739 result = VULKAN_AllocateImage(rendererData, create_props, width, height, textureFormat, usage, imageViewSwizzle, textureData->samplerYcbcrConversion, &textureData->mainImage);
2740 if (result != VK_SUCCESS) {
2741 SET_ERROR_CODE("VULKAN_AllocateImage()", result);
2742 return false;
2743 }
2744
2745 SDL_PropertiesID props = SDL_GetTextureProperties(texture);
2746 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER, (Sint64)textureData->mainImage.image);
2747
2748 if (texture->access == SDL_TEXTUREACCESS_TARGET) {
2749 result = VULKAN_CreateFramebuffersAndRenderPasses(renderer,
2750 texture->w,
2751 texture->h,
2752 textureFormat,
2753 1,
2754 &textureData->mainImage.imageView,
2755 &textureData->mainFramebuffer,
2756 textureData->mainRenderpasses);
2757 if (result != VK_SUCCESS) {
2758 SET_ERROR_CODE("VULKAN_CreateFramebuffersAndRenderPasses()", result);
2759 return false;
2760 }
2761 }
2762 return true;
2763}
2764
2765static void VULKAN_DestroyTexture(SDL_Renderer *renderer,
2766 SDL_Texture *texture)
2767{
2768 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
2769 VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->internal;
2770
2771 if (!textureData) {
2772 return;
2773 }
2774
2775 /* Because SDL_DestroyTexture might be called while the data is in-flight, we need to issue the batch first
2776 Unfortunately, this means that deleting a lot of textures mid-frame will have poor performance. */
2777 VULKAN_IssueBatch(rendererData);
2778 VULKAN_WaitForGPU(rendererData);
2779
2780 VULKAN_DestroyImage(rendererData, &textureData->mainImage);
2781
2782#ifdef SDL_HAVE_YUV
2783 if (textureData->samplerYcbcrConversion != VK_NULL_HANDLE) {
2784 vkDestroySamplerYcbcrConversionKHR(rendererData->device, textureData->samplerYcbcrConversion, NULL);
2785 textureData->samplerYcbcrConversion = VK_NULL_HANDLE;
2786 }
2787 if (textureData->samplerYcbcr != VK_NULL_HANDLE) {
2788 vkDestroySampler(rendererData->device, textureData->samplerYcbcr, NULL);
2789 textureData->samplerYcbcr = VK_NULL_HANDLE;
2790 }
2791 if (textureData->pipelineLayoutYcbcr != VK_NULL_HANDLE) {
2792 vkDestroyPipelineLayout(rendererData->device, textureData->pipelineLayoutYcbcr, NULL);
2793 textureData->pipelineLayoutYcbcr = VK_NULL_HANDLE;
2794 }
2795 if (textureData->descriptorSetLayoutYcbcr != VK_NULL_HANDLE) {
2796 vkDestroyDescriptorSetLayout(rendererData->device, textureData->descriptorSetLayoutYcbcr, NULL);
2797 textureData->descriptorSetLayoutYcbcr = VK_NULL_HANDLE;
2798 }
2799#endif
2800
2801 VULKAN_DestroyBuffer(rendererData, &textureData->stagingBuffer);
2802 if (textureData->mainFramebuffer != VK_NULL_HANDLE) {
2803 vkDestroyFramebuffer(rendererData->device, textureData->mainFramebuffer, NULL);
2804 textureData->mainFramebuffer = VK_NULL_HANDLE;
2805 }
2806 for (uint32_t i = 0; i < SDL_arraysize(textureData->mainRenderpasses); i++) {
2807 if (textureData->mainRenderpasses[i] != VK_NULL_HANDLE) {
2808 vkDestroyRenderPass(rendererData->device, textureData->mainRenderpasses[i], NULL);
2809 textureData->mainRenderpasses[i] = VK_NULL_HANDLE;
2810 }
2811 }
2812
2813 SDL_free(textureData);
2814 texture->internal = NULL;
2815}
2816
2817static bool VULKAN_UpdateTextureInternal(VULKAN_RenderData *rendererData, VkImage image, VkFormat format, int plane, int x, int y, int w, int h, const void *pixels, int pitch, VkImageLayout *imageLayout)
2818{
2819 VkDeviceSize pixelSize = VULKAN_GetBytesPerPixel(format);
2820 VkDeviceSize length = w * pixelSize;
2821 VkDeviceSize uploadBufferSize = length * h;
2822 const Uint8 *src;
2823 Uint8 *dst;
2824 VkResult rc;
2825 int planeCount = VULKAN_VkFormatGetNumPlanes(format);
2826
2827 VULKAN_EnsureCommandBuffer(rendererData);
2828
2829 int currentUploadBufferIndex = rendererData->currentUploadBuffer[rendererData->currentCommandBufferIndex];
2830 VULKAN_Buffer *uploadBuffer = &rendererData->uploadBuffers[rendererData->currentCommandBufferIndex][currentUploadBufferIndex];
2831
2832 rc = VULKAN_AllocateBuffer(rendererData, uploadBufferSize,
2833 VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
2834 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
2835 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
2836 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
2837 uploadBuffer);
2838 if (rc != VK_SUCCESS) {
2839 return false;
2840 }
2841
2842 src = (const Uint8 *)pixels;
2843 dst = (Uint8 *)uploadBuffer->mappedBufferPtr;
2844 if (length == (VkDeviceSize)pitch) {
2845 SDL_memcpy(dst, src, (size_t)length * h);
2846 } else {
2847 if (length > (VkDeviceSize)pitch) {
2848 length = pitch;
2849 }
2850 for (VkDeviceSize row = h; row--; ) {
2851 SDL_memcpy(dst, src, (size_t)length);
2852 src += pitch;
2853 dst += length;
2854 }
2855 }
2856
2857 // Make sure the destination is in the correct resource state
2858 VULKAN_RecordPipelineImageBarrier(rendererData,
2859 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
2860 VK_ACCESS_TRANSFER_WRITE_BIT,
2861 VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
2862 VK_PIPELINE_STAGE_TRANSFER_BIT,
2863 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
2864 image,
2865 imageLayout);
2866
2867 VkBufferImageCopy region;
2868 region.bufferOffset = 0;
2869 region.bufferRowLength = 0;
2870 region.bufferImageHeight = 0;
2871 region.imageSubresource.baseArrayLayer = 0;
2872 region.imageSubresource.layerCount = 1;
2873 region.imageSubresource.mipLevel = 0;
2874 if (planeCount <= 1) {
2875 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2876 } else {
2877 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_PLANE_0_BIT << plane;
2878 }
2879 region.imageOffset.x = x;
2880 region.imageOffset.y = y;
2881 region.imageOffset.z = 0;
2882 region.imageExtent.width = w;
2883 region.imageExtent.height = h;
2884 region.imageExtent.depth = 1;
2885
2886 vkCmdCopyBufferToImage(rendererData->currentCommandBuffer, uploadBuffer->buffer, image, *imageLayout, 1, &region);
2887
2888 // Transition the texture to be shader accessible
2889 VULKAN_RecordPipelineImageBarrier(rendererData,
2890 VK_ACCESS_TRANSFER_WRITE_BIT,
2891 VK_ACCESS_SHADER_READ_BIT,
2892 VK_PIPELINE_STAGE_TRANSFER_BIT,
2893 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
2894 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
2895 image,
2896 imageLayout);
2897
2898 rendererData->currentUploadBuffer[rendererData->currentCommandBufferIndex]++;
2899
2900 // If we've used up all the upload buffers, we need to issue the batch
2901 if (rendererData->currentUploadBuffer[rendererData->currentCommandBufferIndex] == SDL_VULKAN_NUM_UPLOAD_BUFFERS) {
2902 VULKAN_IssueBatch(rendererData);
2903 }
2904
2905 return true;
2906}
2907
2908
2909static bool VULKAN_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
2910 const SDL_Rect *rect, const void *srcPixels,
2911 int srcPitch)
2912{
2913 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
2914 VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->internal;
2915
2916 if (!textureData) {
2917 return SDL_SetError("Texture is not currently available");
2918 }
2919
2920 if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 0, rect->x, rect->y, rect->w, rect->h, srcPixels, srcPitch, &textureData->mainImage.imageLayout)) {
2921 return false;
2922 }
2923#ifdef SDL_HAVE_YUV
2924 Uint32 numPlanes = VULKAN_VkFormatGetNumPlanes(textureData->mainImage.format);
2925 // Skip to the correct offset into the next texture
2926 srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch);
2927 // YUV data
2928 if (numPlanes == 3) {
2929 for (Uint32 plane = 1; plane < numPlanes; plane++) {
2930 if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, plane, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1) / 2, &textureData->mainImage.imageLayout)) {
2931 return false;
2932 }
2933
2934 // Skip to the correct offset into the next texture
2935 srcPixels = (const void *)((const Uint8 *)srcPixels + ((rect->h + 1) / 2) * ((srcPitch + 1) / 2));
2936 }
2937 }
2938 // NV12/NV21 data
2939 else if (numPlanes == 2)
2940 {
2941 if (texture->format == SDL_PIXELFORMAT_P010) {
2942 srcPitch = (srcPitch + 3) & ~3;
2943 } else {
2944 srcPitch = (srcPitch + 1) & ~1;
2945 }
2946
2947 if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 1, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, srcPitch, &textureData->mainImage.imageLayout)) {
2948 return false;
2949 }
2950 }
2951#endif
2952 return true;
2953}
2954
2955#ifdef SDL_HAVE_YUV
2956static bool VULKAN_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
2957 const SDL_Rect *rect,
2958 const Uint8 *Yplane, int Ypitch,
2959 const Uint8 *Uplane, int Upitch,
2960 const Uint8 *Vplane, int Vpitch)
2961{
2962 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
2963 VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->internal;
2964
2965 if (!textureData) {
2966 return SDL_SetError("Texture is not currently available");
2967 }
2968
2969 if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainImage.imageLayout)) {
2970 return false;
2971 }
2972 if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 1, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Uplane, Upitch, &textureData->mainImage.imageLayout)) {
2973 return false;
2974 }
2975 if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 2, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Vplane, Vpitch, &textureData->mainImage.imageLayout)) {
2976 return false;
2977 }
2978 return true;
2979}
2980
2981static bool VULKAN_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture,
2982 const SDL_Rect *rect,
2983 const Uint8 *Yplane, int Ypitch,
2984 const Uint8 *UVplane, int UVpitch)
2985{
2986 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
2987 VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->internal;
2988
2989 if (!textureData) {
2990 return SDL_SetError("Texture is not currently available");
2991 }
2992
2993 if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainImage.imageLayout)) {
2994 return false;
2995 }
2996
2997 if (!VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 1, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, UVplane, UVpitch, &textureData->mainImage.imageLayout)) {
2998 return false;
2999 }
3000 return true;
3001}
3002#endif
3003
3004static bool VULKAN_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture,
3005 const SDL_Rect *rect, void **pixels, int *pitch)
3006{
3007 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
3008 VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->internal;
3009 VkResult rc;
3010 if (!textureData) {
3011 return SDL_SetError("Texture is not currently available");
3012 }
3013
3014 if (textureData->stagingBuffer.buffer != VK_NULL_HANDLE) {
3015 return SDL_SetError("texture is already locked");
3016 }
3017
3018 VkDeviceSize pixelSize = VULKAN_GetBytesPerPixel(textureData->mainImage.format);
3019 VkDeviceSize length = rect->w * pixelSize;
3020 VkDeviceSize stagingBufferSize = length * rect->h;
3021 rc = VULKAN_AllocateBuffer(rendererData,
3022 stagingBufferSize,
3023 VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
3024 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
3025 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
3026 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
3027 &textureData->stagingBuffer);
3028 if (rc != VK_SUCCESS) {
3029 return false;
3030 }
3031
3032 /* Make note of where the staging texture will be written to
3033 * (on a call to SDL_UnlockTexture):
3034 */
3035 textureData->lockedRect = *rect;
3036
3037 /* Make sure the caller has information on the texture's pixel buffer,
3038 * then return:
3039 */
3040 *pixels = textureData->stagingBuffer.mappedBufferPtr;
3041 *pitch = (int)length;
3042 return true;
3043
3044}
3045
3046static void VULKAN_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
3047{
3048 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
3049 VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->internal;
3050
3051 if (!textureData) {
3052 return;
3053 }
3054
3055 VULKAN_EnsureCommandBuffer(rendererData);
3056
3057 // Make sure the destination is in the correct resource state
3058 VULKAN_RecordPipelineImageBarrier(rendererData,
3059 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
3060 VK_ACCESS_TRANSFER_WRITE_BIT,
3061 VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
3062 VK_PIPELINE_STAGE_TRANSFER_BIT,
3063 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
3064 textureData->mainImage.image,
3065 &textureData->mainImage.imageLayout);
3066
3067 VkBufferImageCopy region;
3068 region.bufferOffset = 0;
3069 region.bufferRowLength = 0;
3070 region.bufferImageHeight = 0;
3071 region.imageSubresource.baseArrayLayer = 0;
3072 region.imageSubresource.layerCount = 1;
3073 region.imageSubresource.mipLevel = 0;
3074 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3075 region.imageOffset.x = textureData->lockedRect.x;
3076 region.imageOffset.y = textureData->lockedRect.y;
3077 region.imageOffset.z = 0;
3078 region.imageExtent.width = textureData->lockedRect.w;
3079 region.imageExtent.height = textureData->lockedRect.h;
3080 region.imageExtent.depth = 1;
3081 vkCmdCopyBufferToImage(rendererData->currentCommandBuffer, textureData->stagingBuffer.buffer, textureData->mainImage.image, textureData->mainImage.imageLayout, 1, &region);
3082
3083 // Transition the texture to be shader accessible
3084 VULKAN_RecordPipelineImageBarrier(rendererData,
3085 VK_ACCESS_TRANSFER_WRITE_BIT,
3086 VK_ACCESS_SHADER_READ_BIT,
3087 VK_PIPELINE_STAGE_TRANSFER_BIT,
3088 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
3089 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
3090 textureData->mainImage.image,
3091 &textureData->mainImage.imageLayout);
3092
3093 // Execute the command list before releasing the staging buffer
3094 VULKAN_IssueBatch(rendererData);
3095
3096 VULKAN_DestroyBuffer(rendererData, &textureData->stagingBuffer);
3097}
3098
3099static bool VULKAN_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
3100{
3101 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
3102 VULKAN_TextureData *textureData = NULL;
3103
3104 VULKAN_EnsureCommandBuffer(rendererData);
3105
3106 if (!texture) {
3107 if (rendererData->textureRenderTarget) {
3108
3109 VULKAN_RecordPipelineImageBarrier(rendererData,
3110 VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
3111 VK_ACCESS_SHADER_READ_BIT,
3112 VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
3113 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
3114 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
3115 rendererData->textureRenderTarget->mainImage.image,
3116 &rendererData->textureRenderTarget->mainImage.imageLayout);
3117 }
3118 rendererData->textureRenderTarget = NULL;
3119 return true;
3120 }
3121
3122 textureData = (VULKAN_TextureData *)texture->internal;
3123
3124 if (textureData->mainImage.imageView == VK_NULL_HANDLE) {
3125 return SDL_SetError("specified texture is not a render target");
3126 }
3127
3128 rendererData->textureRenderTarget = textureData;
3129 VULKAN_RecordPipelineImageBarrier(rendererData,
3130 VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
3131 VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
3132 VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
3133 VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
3134 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
3135 rendererData->textureRenderTarget->mainImage.image,
3136 &rendererData->textureRenderTarget->mainImage.imageLayout);
3137
3138 return true;
3139}
3140
3141static bool VULKAN_QueueNoOp(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
3142{
3143 return true; // nothing to do in this backend.
3144}
3145
3146static bool VULKAN_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count)
3147{
3148 VULKAN_VertexPositionColor *verts = (VULKAN_VertexPositionColor *)SDL_AllocateRenderVertices(renderer, count * sizeof(VULKAN_VertexPositionColor), 0, &cmd->data.draw.first);
3149 int i;
3150 bool convert_color = SDL_RenderingLinearSpace(renderer);
3151
3152 if (!verts) {
3153 return false;
3154 }
3155
3156 cmd->data.draw.count = count;
3157 for (i = 0; i < count; i++) {
3158 verts->pos[0] = points[i].x + 0.5f;
3159 verts->pos[1] = points[i].y + 0.5f;
3160 verts->tex[0] = 0.0f;
3161 verts->tex[1] = 0.0f;
3162 verts->color = cmd->data.draw.color;
3163 if (convert_color) {
3164 SDL_ConvertToLinear(&verts->color);
3165 }
3166 verts++;
3167 }
3168 return true;
3169}
3170
3171static bool VULKAN_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture,
3172 const float *xy, int xy_stride, const SDL_FColor *color, int color_stride, const float *uv, int uv_stride,
3173 int num_vertices, const void *indices, int num_indices, int size_indices,
3174 float scale_x, float scale_y)
3175{
3176 int i;
3177 int count = indices ? num_indices : num_vertices;
3178 VULKAN_VertexPositionColor *verts = (VULKAN_VertexPositionColor *)SDL_AllocateRenderVertices(renderer, count * sizeof(VULKAN_VertexPositionColor), 0, &cmd->data.draw.first);
3179 bool convert_color = SDL_RenderingLinearSpace(renderer);
3180 VULKAN_TextureData *textureData = texture ? (VULKAN_TextureData *)texture->internal : NULL;
3181 float u_scale = textureData ? (float)texture->w / textureData->width : 0.0f;
3182 float v_scale = textureData ? (float)texture->h / textureData->height : 0.0f;
3183
3184 if (!verts) {
3185 return false;
3186 }
3187
3188 cmd->data.draw.count = count;
3189 size_indices = indices ? size_indices : 0;
3190
3191 for (i = 0; i < count; i++) {
3192 int j;
3193 float *xy_;
3194 if (size_indices == 4) {
3195 j = ((const Uint32 *)indices)[i];
3196 } else if (size_indices == 2) {
3197 j = ((const Uint16 *)indices)[i];
3198 } else if (size_indices == 1) {
3199 j = ((const Uint8 *)indices)[i];
3200 } else {
3201 j = i;
3202 }
3203
3204 xy_ = (float *)((char *)xy + j * xy_stride);
3205
3206 verts->pos[0] = xy_[0] * scale_x;
3207 verts->pos[1] = xy_[1] * scale_y;
3208 verts->color = *(SDL_FColor *)((char *)color + j * color_stride);
3209 if (convert_color) {
3210 SDL_ConvertToLinear(&verts->color);
3211 }
3212
3213 if (texture) {
3214 float *uv_ = (float *)((char *)uv + j * uv_stride);
3215 verts->tex[0] = uv_[0] * u_scale;
3216 verts->tex[1] = uv_[1] * v_scale;
3217 } else {
3218 verts->tex[0] = 0.0f;
3219 verts->tex[1] = 0.0f;
3220 }
3221
3222 verts += 1;
3223 }
3224 return true;
3225}
3226
3227static bool VULKAN_UpdateVertexBuffer(SDL_Renderer *renderer,
3228 const void *vertexData, size_t dataSizeInBytes, VULKAN_DrawStateCache *stateCache)
3229{
3230 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
3231 const int vbidx = rendererData->currentVertexBuffer;
3232 VULKAN_Buffer *vertexBuffer;
3233
3234 if (dataSizeInBytes == 0) {
3235 return true; // nothing to do.
3236 }
3237
3238 if (rendererData->issueBatch) {
3239 if (VULKAN_IssueBatch(rendererData) != VK_SUCCESS) {
3240 return SDL_SetError("Failed to issue intermediate batch");
3241 }
3242 }
3243 // If the existing vertex buffer isn't big enough, we need to recreate a big enough one
3244 if (dataSizeInBytes > rendererData->vertexBuffers[vbidx].size) {
3245 VULKAN_IssueBatch(rendererData);
3246 VULKAN_WaitForGPU(rendererData);
3247 VULKAN_CreateVertexBuffer(rendererData, vbidx, dataSizeInBytes);
3248 }
3249
3250 vertexBuffer = &rendererData->vertexBuffers[vbidx];
3251 SDL_memcpy(vertexBuffer->mappedBufferPtr, vertexData, dataSizeInBytes);
3252
3253 stateCache->vertexBuffer = vertexBuffer->buffer;
3254
3255 rendererData->currentVertexBuffer = vbidx + 1;
3256 if (rendererData->currentVertexBuffer >= SDL_VULKAN_NUM_VERTEX_BUFFERS) {
3257 rendererData->currentVertexBuffer = 0;
3258 rendererData->issueBatch = true;
3259 }
3260
3261 return true;
3262}
3263
3264static VkSurfaceTransformFlagBitsKHR VULKAN_GetRotationForCurrentRenderTarget(VULKAN_RenderData *rendererData)
3265{
3266 if (rendererData->textureRenderTarget) {
3267 return VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
3268 } else {
3269 return rendererData->swapChainPreTransform;
3270 }
3271}
3272
3273static bool VULKAN_IsDisplayRotated90Degrees(VkSurfaceTransformFlagBitsKHR rotation)
3274{
3275 switch (rotation) {
3276 case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
3277 case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
3278 return true;
3279 default:
3280 return false;
3281 }
3282}
3283
3284static bool VULKAN_UpdateViewport(SDL_Renderer *renderer)
3285{
3286 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
3287 const SDL_Rect *viewport = &rendererData->currentViewport;
3288 Float4X4 projection;
3289 Float4X4 view;
3290 VkSurfaceTransformFlagBitsKHR rotation = VULKAN_GetRotationForCurrentRenderTarget(rendererData);
3291 bool swapDimensions;
3292
3293 if (viewport->w == 0 || viewport->h == 0) {
3294 /* If the viewport is empty, assume that it is because
3295 * SDL_CreateRenderer is calling it, and will call it again later
3296 * with a non-empty viewport.
3297 */
3298 // SDL_Log("%s, no viewport was set!", __FUNCTION__);
3299 return false;
3300 }
3301
3302 switch (rotation) {
3303 case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
3304 projection = MatrixRotationZ(SDL_PI_F * 0.5f);
3305 break;
3306 case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
3307 projection = MatrixRotationZ(SDL_PI_F);
3308 break;
3309 case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
3310 projection = MatrixRotationZ(-SDL_PI_F * 0.5f);
3311 break;
3312 case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
3313 default:
3314 projection = MatrixIdentity();
3315 break;
3316
3317 }
3318
3319 // Update the view matrix
3320 SDL_zero(view);
3321 view.m[0][0] = 2.0f / viewport->w;
3322 view.m[1][1] = -2.0f / viewport->h;
3323 view.m[2][2] = 1.0f;
3324 view.m[3][0] = -1.0f;
3325 view.m[3][1] = 1.0f;
3326 view.m[3][3] = 1.0f;
3327
3328 rendererData->vertexShaderConstantsData.projectionAndView = MatrixMultiply(
3329 view,
3330 projection);
3331
3332 VkViewport vkViewport;
3333
3334 swapDimensions = VULKAN_IsDisplayRotated90Degrees(rotation);
3335 if (swapDimensions) {
3336 vkViewport.x = viewport->y;
3337 vkViewport.y = viewport->x;
3338 vkViewport.width = viewport->h;
3339 vkViewport.height = viewport->w;
3340 }
3341 else {
3342 vkViewport.x = viewport->x;
3343 vkViewport.y = viewport->y;
3344 vkViewport.width = viewport->w;
3345 vkViewport.height = viewport->h;
3346 }
3347 vkViewport.minDepth = 0.0f;
3348 vkViewport.maxDepth = 1.0f;
3349 vkCmdSetViewport(rendererData->currentCommandBuffer, 0, 1, &vkViewport);
3350
3351 rendererData->viewportDirty = false;
3352 return true;
3353}
3354
3355static bool VULKAN_UpdateClipRect(SDL_Renderer *renderer)
3356{
3357 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
3358 const SDL_Rect *viewport = &rendererData->currentViewport;
3359 VkSurfaceTransformFlagBitsKHR rotation = VULKAN_GetRotationForCurrentRenderTarget(rendererData);
3360 bool swapDimensions = VULKAN_IsDisplayRotated90Degrees(rotation);
3361
3362 VkRect2D scissor;
3363 if (rendererData->currentCliprectEnabled) {
3364 scissor.offset.x = viewport->x + rendererData->currentCliprect.x;
3365 scissor.offset.y = viewport->y + rendererData->currentCliprect.y;
3366 scissor.extent.width = rendererData->currentCliprect.w;
3367 scissor.extent.height = rendererData->currentCliprect.h;
3368 } else {
3369 scissor.offset.x = viewport->x;
3370 scissor.offset.y = viewport->y;
3371 scissor.extent.width = viewport->w;
3372 scissor.extent.height = viewport->h;
3373 }
3374 if (swapDimensions) {
3375 VkRect2D scissorTemp = scissor;
3376 scissor.offset.x = scissorTemp.offset.y;
3377 scissor.offset.y = scissorTemp.offset.x;
3378 scissor.extent.width = scissorTemp.extent.height;
3379 scissor.extent.height = scissorTemp.extent.width;
3380 }
3381 vkCmdSetScissor(rendererData->currentCommandBuffer, 0, 1, &scissor);
3382
3383 rendererData->cliprectDirty = false;
3384 return true;
3385}
3386
3387static void VULKAN_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_Texture *texture, VULKAN_PixelShaderConstants *constants)
3388{
3389 float output_headroom;
3390
3391 SDL_zerop(constants);
3392
3393 constants->scRGB_output = (float)SDL_RenderingLinearSpace(renderer);
3394 constants->color_scale = cmd->data.draw.color_scale;
3395
3396 if (texture) {
3397 switch (texture->format) {
3398 case SDL_PIXELFORMAT_YV12:
3399 case SDL_PIXELFORMAT_IYUV:
3400 case SDL_PIXELFORMAT_NV12:
3401 case SDL_PIXELFORMAT_NV21:
3402 constants->input_type = INPUTTYPE_SRGB;
3403 break;
3404 case SDL_PIXELFORMAT_P010:
3405 constants->input_type = INPUTTYPE_HDR10;
3406 break;
3407 default:
3408 if (texture->colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
3409 constants->input_type = INPUTTYPE_SCRGB;
3410 } else if (SDL_COLORSPACEPRIMARIES(texture->colorspace) == SDL_COLOR_PRIMARIES_BT2020 &&
3411 SDL_COLORSPACETRANSFER(texture->colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) {
3412 constants->input_type = INPUTTYPE_HDR10;
3413 } else {
3414 // The sampler will convert from sRGB to linear on load if working in linear colorspace
3415 constants->input_type = INPUTTYPE_UNSPECIFIED;
3416 }
3417 break;
3418 }
3419
3420 if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_PIXELART) {
3421 constants->pixel_art = 1.0f;
3422 constants->texture_width = texture->w;
3423 constants->texture_height = texture->h;
3424 constants->texel_width = 1.0f / constants->texture_width;
3425 constants->texel_height = 1.0f / constants->texture_height;
3426 }
3427
3428 constants->sdr_white_point = texture->SDR_white_point;
3429
3430 if (renderer->target) {
3431 output_headroom = renderer->target->HDR_headroom;
3432 } else {
3433 output_headroom = renderer->HDR_headroom;
3434 }
3435
3436 if (texture->HDR_headroom > output_headroom) {
3437 constants->tonemap_method = TONEMAP_CHROME;
3438 constants->tonemap_factor1 = (output_headroom / (texture->HDR_headroom * texture->HDR_headroom));
3439 constants->tonemap_factor2 = (1.0f / output_headroom);
3440 }
3441 }
3442}
3443
3444static VkDescriptorPool VULKAN_AllocateDescriptorPool(VULKAN_RenderData *rendererData)
3445{
3446 VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
3447 VkDescriptorPoolSize descriptorPoolSizes[3];
3448 VkResult result;
3449 descriptorPoolSizes[0].descriptorCount = SDL_VULKAN_MAX_DESCRIPTOR_SETS;
3450 descriptorPoolSizes[0].type = VK_DESCRIPTOR_TYPE_SAMPLER;
3451
3452 descriptorPoolSizes[1].descriptorCount = SDL_VULKAN_MAX_DESCRIPTOR_SETS;
3453 descriptorPoolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
3454
3455 descriptorPoolSizes[2].descriptorCount = SDL_VULKAN_MAX_DESCRIPTOR_SETS;
3456 descriptorPoolSizes[2].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
3457
3458 VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = { 0 };
3459 descriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
3460 descriptorPoolCreateInfo.poolSizeCount = SDL_arraysize(descriptorPoolSizes);
3461 descriptorPoolCreateInfo.pPoolSizes = descriptorPoolSizes;
3462 descriptorPoolCreateInfo.maxSets = SDL_VULKAN_MAX_DESCRIPTOR_SETS;
3463 result = vkCreateDescriptorPool(rendererData->device, &descriptorPoolCreateInfo, NULL, &descriptorPool);
3464 if (result != VK_SUCCESS) {
3465 SET_ERROR_CODE("vkCreateDescrptorPool()", result);
3466 return VK_NULL_HANDLE;
3467 }
3468
3469 return descriptorPool;
3470}
3471
3472static VkResult VULKAN_CreateDescriptorSetAndPipelineLayout(VULKAN_RenderData *rendererData, VkSampler samplerYcbcr, VkDescriptorSetLayout *descriptorSetLayoutOut,
3473 VkPipelineLayout *pipelineLayoutOut)
3474{
3475 VkResult result;
3476
3477 // Descriptor set layout
3478 VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = { 0 };
3479 descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
3480 descriptorSetLayoutCreateInfo.flags = 0;
3481 VkDescriptorSetLayoutBinding layoutBindings[2];
3482 // PixelShaderConstants
3483 layoutBindings[0].binding = 1;
3484 layoutBindings[0].descriptorCount = 1;
3485 layoutBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
3486 layoutBindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
3487 layoutBindings[0].pImmutableSamplers = NULL;
3488
3489 // Combined image/sampler
3490 layoutBindings[1].binding = 0;
3491 layoutBindings[1].descriptorCount = 1;
3492 layoutBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
3493 layoutBindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
3494 layoutBindings[1].pImmutableSamplers = (samplerYcbcr != VK_NULL_HANDLE) ? &samplerYcbcr : NULL;
3495
3496 descriptorSetLayoutCreateInfo.bindingCount = 2;
3497 descriptorSetLayoutCreateInfo.pBindings = layoutBindings;
3498 result = vkCreateDescriptorSetLayout(rendererData->device, &descriptorSetLayoutCreateInfo, NULL, descriptorSetLayoutOut);
3499 if (result != VK_SUCCESS) {
3500 SET_ERROR_CODE("vkCreateDescriptorSetLayout()", result);
3501 return result;
3502 }
3503
3504 // Pipeline layout
3505 VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { 0 };
3506 VkPushConstantRange pushConstantRange;
3507 pushConstantRange.size = sizeof( VULKAN_VertexShaderConstants );
3508 pushConstantRange.offset = 0;
3509 pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
3510 pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
3511 pipelineLayoutCreateInfo.setLayoutCount = 1;
3512 pipelineLayoutCreateInfo.pSetLayouts = descriptorSetLayoutOut;
3513 pipelineLayoutCreateInfo.pushConstantRangeCount = 1;
3514 pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange;
3515 result = vkCreatePipelineLayout(rendererData->device, &pipelineLayoutCreateInfo, NULL, pipelineLayoutOut);
3516 if (result != VK_SUCCESS) {
3517 SET_ERROR_CODE("vkCreatePipelineLayout()", result);
3518 return result;
3519 }
3520
3521 return result;
3522}
3523
3524static VkDescriptorSet VULKAN_AllocateDescriptorSet(SDL_Renderer *renderer, VULKAN_Shader shader, VkDescriptorSetLayout descriptorSetLayout,
3525 VkSampler sampler, VkBuffer constantBuffer, VkDeviceSize constantBufferOffset, VkImageView imageView)
3526{
3527 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
3528 uint32_t currentDescriptorPoolIndex = rendererData->currentDescriptorPoolIndex;
3529 VkDescriptorPool descriptorPool = rendererData->descriptorPools[rendererData->currentCommandBufferIndex][currentDescriptorPoolIndex];
3530
3531 VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = { 0 };
3532 descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
3533 descriptorSetAllocateInfo.descriptorSetCount = 1;
3534 descriptorSetAllocateInfo.descriptorPool = descriptorPool;
3535 descriptorSetAllocateInfo.pSetLayouts = &descriptorSetLayout;
3536
3537 VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
3538 VkResult result = (rendererData->currentDescriptorSetIndex >= SDL_VULKAN_MAX_DESCRIPTOR_SETS) ? VK_ERROR_OUT_OF_DEVICE_MEMORY : VK_SUCCESS;
3539 if (result == VK_SUCCESS) {
3540 result = vkAllocateDescriptorSets(rendererData->device, &descriptorSetAllocateInfo, &descriptorSet);
3541 }
3542 if (result != VK_SUCCESS) {
3543 // Out of descriptor sets in this pool - see if we have more pools allocated
3544 currentDescriptorPoolIndex++;
3545 if (currentDescriptorPoolIndex < rendererData->numDescriptorPools[rendererData->currentCommandBufferIndex]) {
3546 descriptorPool = rendererData->descriptorPools[rendererData->currentCommandBufferIndex][currentDescriptorPoolIndex];
3547 descriptorSetAllocateInfo.descriptorPool = descriptorPool;
3548 result = vkAllocateDescriptorSets(rendererData->device, &descriptorSetAllocateInfo, &descriptorSet);
3549 if (result != VK_SUCCESS) {
3550 // This should not fail - we are allocating from the front of the descriptor set
3551 SDL_SetError("Unable to allocate descriptor set");
3552 return VK_NULL_HANDLE;
3553 }
3554 rendererData->currentDescriptorPoolIndex = currentDescriptorPoolIndex;
3555 rendererData->currentDescriptorSetIndex = 0;
3556
3557 }
3558 // We are out of pools, create a new one
3559 else {
3560 descriptorPool = VULKAN_AllocateDescriptorPool(rendererData);
3561 if (descriptorPool == VK_NULL_HANDLE) {
3562 // SDL_SetError called in VULKAN_AllocateDescriptorPool if we failed to allocate a new pool
3563 return VK_NULL_HANDLE;
3564 }
3565 rendererData->numDescriptorPools[rendererData->currentCommandBufferIndex]++;
3566 VkDescriptorPool *descriptorPools = (VkDescriptorPool *)SDL_realloc(rendererData->descriptorPools[rendererData->currentCommandBufferIndex],
3567 sizeof(VkDescriptorPool) * rendererData->numDescriptorPools[rendererData->currentCommandBufferIndex]);
3568 descriptorPools[rendererData->numDescriptorPools[rendererData->currentCommandBufferIndex] - 1] = descriptorPool;
3569 rendererData->descriptorPools[rendererData->currentCommandBufferIndex] = descriptorPools;
3570 rendererData->currentDescriptorPoolIndex = currentDescriptorPoolIndex;
3571 rendererData->currentDescriptorSetIndex = 0;
3572
3573 // Call recursively to allocate from the new pool
3574 return VULKAN_AllocateDescriptorSet(renderer, shader, descriptorSetLayout, sampler, constantBuffer, constantBufferOffset, imageView);
3575 }
3576 }
3577 rendererData->currentDescriptorSetIndex++;
3578 VkDescriptorImageInfo combinedImageSamplerDescriptor = { 0 };
3579 VkDescriptorBufferInfo bufferDescriptor = { 0 };
3580 bufferDescriptor.buffer = constantBuffer;
3581 bufferDescriptor.offset = constantBufferOffset;
3582 bufferDescriptor.range = sizeof(VULKAN_PixelShaderConstants);
3583
3584 VkWriteDescriptorSet descriptorWrites[2];
3585 SDL_memset(descriptorWrites, 0, sizeof(descriptorWrites));
3586 uint32_t descriptorCount = 1; // Always have the uniform buffer
3587
3588 descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
3589 descriptorWrites[0].dstSet = descriptorSet;
3590 descriptorWrites[0].dstBinding = 1;
3591 descriptorWrites[0].dstArrayElement = 0;
3592 descriptorWrites[0].descriptorCount = 1;
3593 descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
3594 descriptorWrites[0].pBufferInfo = &bufferDescriptor;
3595
3596 if (sampler != VK_NULL_HANDLE && imageView != VK_NULL_HANDLE) {
3597 descriptorCount++;
3598 descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
3599 descriptorWrites[1].dstSet = descriptorSet;
3600 descriptorWrites[1].dstBinding = 0;
3601 descriptorWrites[1].dstArrayElement = 0;
3602 descriptorWrites[1].descriptorCount = 1;
3603 descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
3604 descriptorWrites[1].pImageInfo = &combinedImageSamplerDescriptor;
3605
3606 // Ignore the sampler if we're using YcBcCr data since it will be baked in the descriptor set layout
3607 if (descriptorSetLayout == rendererData->descriptorSetLayout) {
3608 combinedImageSamplerDescriptor.sampler = sampler;
3609 }
3610 combinedImageSamplerDescriptor.imageView = imageView;
3611 combinedImageSamplerDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
3612 }
3613
3614 vkUpdateDescriptorSets(rendererData->device, descriptorCount, descriptorWrites, 0, NULL);
3615
3616 return descriptorSet;
3617}
3618
3619static bool VULKAN_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, VULKAN_Shader shader, VkPipelineLayout pipelineLayout, VkDescriptorSetLayout descriptorSetLayout,
3620 const VULKAN_PixelShaderConstants *shader_constants, VkPrimitiveTopology topology, VkImageView imageView, VkSampler sampler, const Float4X4 *matrix, VULKAN_DrawStateCache *stateCache)
3621
3622{
3623 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
3624 const SDL_BlendMode blendMode = cmd->data.draw.blend;
3625 VkFormat format = rendererData->surfaceFormat.format;
3626 const Float4X4 *newmatrix = matrix ? matrix : &rendererData->identity;
3627 bool updateConstants = false;
3628 VULKAN_PixelShaderConstants solid_constants;
3629 VkDescriptorSet descriptorSet;
3630 VkBuffer constantBuffer;
3631 VkDeviceSize constantBufferOffset;
3632 int i;
3633
3634 if (!VULKAN_ActivateCommandBuffer(renderer, VK_ATTACHMENT_LOAD_OP_LOAD, NULL, stateCache)) {
3635 return false;
3636 }
3637
3638 // See if we need to change the pipeline state
3639 if (!rendererData->currentPipelineState ||
3640 rendererData->currentPipelineState->shader != shader ||
3641 rendererData->currentPipelineState->blendMode != blendMode ||
3642 rendererData->currentPipelineState->topology != topology ||
3643 rendererData->currentPipelineState->format != format ||
3644 rendererData->currentPipelineState->pipelineLayout != pipelineLayout ||
3645 rendererData->currentPipelineState->descriptorSetLayout != descriptorSetLayout) {
3646
3647 rendererData->currentPipelineState = NULL;
3648 for (i = 0; i < rendererData->pipelineStateCount; ++i) {
3649 VULKAN_PipelineState *candidatePiplineState = &rendererData->pipelineStates[i];
3650 if (candidatePiplineState->shader == shader &&
3651 candidatePiplineState->blendMode == blendMode &&
3652 candidatePiplineState->topology == topology &&
3653 candidatePiplineState->format == format &&
3654 candidatePiplineState->pipelineLayout == pipelineLayout &&
3655 candidatePiplineState->descriptorSetLayout == descriptorSetLayout) {
3656 rendererData->currentPipelineState = candidatePiplineState;
3657 break;
3658 }
3659 }
3660
3661 // If we didn't find a match, create a new one -- it must mean the blend mode is non-standard
3662 if (!rendererData->currentPipelineState) {
3663 rendererData->currentPipelineState = VULKAN_CreatePipelineState(renderer, shader, pipelineLayout, descriptorSetLayout, blendMode, topology, format);
3664 }
3665
3666 if (!rendererData->currentPipelineState) {
3667 return SDL_SetError("Unable to create required pipeline state");
3668 }
3669
3670 vkCmdBindPipeline(rendererData->currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, rendererData->currentPipelineState->pipeline);
3671 updateConstants = true;
3672 }
3673
3674 if (rendererData->viewportDirty) {
3675 if (VULKAN_UpdateViewport(renderer)) {
3676 // vertexShaderConstantsData.projectionAndView has changed
3677 updateConstants = true;
3678 }
3679 }
3680
3681 if (rendererData->cliprectDirty) {
3682 VULKAN_UpdateClipRect(renderer);
3683 }
3684
3685 if (updateConstants == true || SDL_memcmp(&rendererData->vertexShaderConstantsData.model, newmatrix, sizeof(*newmatrix)) != 0) {
3686 SDL_memcpy(&rendererData->vertexShaderConstantsData.model, newmatrix, sizeof(*newmatrix));
3687 vkCmdPushConstants(rendererData->currentCommandBuffer, rendererData->currentPipelineState->pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0,
3688 sizeof(rendererData->vertexShaderConstantsData),
3689 &rendererData->vertexShaderConstantsData);
3690 }
3691
3692 if (!shader_constants) {
3693 VULKAN_SetupShaderConstants(renderer, cmd, NULL, &solid_constants);
3694 shader_constants = &solid_constants;
3695 }
3696
3697 constantBuffer = rendererData->constantBuffers[rendererData->currentCommandBufferIndex][rendererData->currentConstantBufferIndex].buffer;
3698 constantBufferOffset = (rendererData->currentConstantBufferOffset < 0) ? 0 : rendererData->currentConstantBufferOffset;
3699 if (updateConstants ||
3700 SDL_memcmp(shader_constants, &rendererData->currentPipelineState->shader_constants, sizeof(*shader_constants)) != 0) {
3701
3702 if (rendererData->currentConstantBufferOffset == -1) {
3703 // First time, grab offset 0
3704 rendererData->currentConstantBufferOffset = 0;
3705 constantBufferOffset = 0;
3706 }
3707 else {
3708 // Align the next address to the minUniformBufferOffsetAlignment
3709 VkDeviceSize alignment = rendererData->physicalDeviceProperties.limits.minUniformBufferOffsetAlignment;
3710 SDL_assert(rendererData->currentConstantBufferOffset >= 0 );
3711 rendererData->currentConstantBufferOffset += (int32_t)(sizeof(VULKAN_PixelShaderConstants) + alignment - 1) & ~(alignment - 1);
3712 constantBufferOffset = rendererData->currentConstantBufferOffset;
3713 }
3714
3715 // If we have run out of size in this constant buffer, create another if needed
3716 if (rendererData->currentConstantBufferOffset >= SDL_VULKAN_CONSTANT_BUFFER_DEFAULT_SIZE) {
3717 uint32_t newConstantBufferIndex = (rendererData->currentConstantBufferIndex + 1);
3718 // We need a new constant buffer
3719 if (newConstantBufferIndex >= rendererData->numConstantBuffers[rendererData->currentCommandBufferIndex]) {
3720 VULKAN_Buffer newConstantBuffer;
3721 VkResult result = VULKAN_AllocateBuffer(rendererData,
3722 SDL_VULKAN_CONSTANT_BUFFER_DEFAULT_SIZE,
3723 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
3724 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
3725 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
3726 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
3727 &newConstantBuffer);
3728 if (result != VK_SUCCESS) {
3729 return false;
3730 }
3731
3732 rendererData->numConstantBuffers[rendererData->currentCommandBufferIndex]++;
3733 VULKAN_Buffer *newConstantBuffers = (VULKAN_Buffer *)SDL_realloc(rendererData->constantBuffers[rendererData->currentCommandBufferIndex],
3734 sizeof(VULKAN_Buffer) * rendererData->numConstantBuffers[rendererData->currentCommandBufferIndex]);
3735 newConstantBuffers[rendererData->numConstantBuffers[rendererData->currentCommandBufferIndex] - 1] = newConstantBuffer;
3736 rendererData->constantBuffers[rendererData->currentCommandBufferIndex] = newConstantBuffers;
3737 }
3738 rendererData->currentConstantBufferIndex = newConstantBufferIndex;
3739 rendererData->currentConstantBufferOffset = 0;
3740 constantBufferOffset = 0;
3741 constantBuffer = rendererData->constantBuffers[rendererData->currentCommandBufferIndex][rendererData->currentConstantBufferIndex].buffer;
3742 }
3743
3744 SDL_memcpy(&rendererData->currentPipelineState->shader_constants, shader_constants, sizeof(*shader_constants));
3745
3746 // Upload constants to persistently mapped buffer
3747 uint8_t *dst = (uint8_t *)rendererData->constantBuffers[rendererData->currentCommandBufferIndex][rendererData->currentConstantBufferIndex].mappedBufferPtr;
3748 dst += constantBufferOffset;
3749 SDL_memcpy(dst, &rendererData->currentPipelineState->shader_constants, sizeof(VULKAN_PixelShaderConstants));
3750 }
3751
3752 // Allocate/update descriptor set with the bindings
3753 descriptorSet = VULKAN_AllocateDescriptorSet(renderer, shader, descriptorSetLayout, sampler, constantBuffer, constantBufferOffset, imageView);
3754 if (descriptorSet == VK_NULL_HANDLE) {
3755 return false;
3756 }
3757
3758 // Bind the descriptor set with the sampler/UBO/image views
3759 vkCmdBindDescriptorSets(rendererData->currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, rendererData->currentPipelineState->pipelineLayout,
3760 0, 1, &descriptorSet, 0, NULL);
3761
3762 return true;
3763}
3764
3765
3766static bool VULKAN_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const Float4X4 *matrix, VULKAN_DrawStateCache *stateCache)
3767{
3768 SDL_Texture *texture = cmd->data.draw.texture;
3769 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
3770 VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->internal;
3771 VkSampler textureSampler = VK_NULL_HANDLE;
3772 VULKAN_PixelShaderConstants constants;
3773 VkDescriptorSetLayout descriptorSetLayout = (textureData->descriptorSetLayoutYcbcr != VK_NULL_HANDLE) ? textureData->descriptorSetLayoutYcbcr : rendererData->descriptorSetLayout;
3774 VkPipelineLayout pipelineLayout = (textureData->pipelineLayoutYcbcr != VK_NULL_HANDLE) ? textureData->pipelineLayoutYcbcr : rendererData->pipelineLayout;
3775
3776 VULKAN_SetupShaderConstants(renderer, cmd, texture, &constants);
3777
3778 switch (cmd->data.draw.texture_scale_mode) {
3779 case SDL_SCALEMODE_NEAREST:
3780 switch (cmd->data.draw.texture_address_mode) {
3781 case SDL_TEXTURE_ADDRESS_CLAMP:
3782 textureSampler = rendererData->samplers[VULKAN_SAMPLER_NEAREST_CLAMP];
3783 break;
3784 case SDL_TEXTURE_ADDRESS_WRAP:
3785 textureSampler = rendererData->samplers[VULKAN_SAMPLER_NEAREST_WRAP];
3786 break;
3787 default:
3788 return SDL_SetError("Unknown texture address mode: %d", cmd->data.draw.texture_address_mode);
3789 }
3790 break;
3791 case SDL_SCALEMODE_PIXELART: // Uses linear sampling
3792 case SDL_SCALEMODE_LINEAR:
3793 switch (cmd->data.draw.texture_address_mode) {
3794 case SDL_TEXTURE_ADDRESS_CLAMP:
3795 textureSampler = rendererData->samplers[VULKAN_SAMPLER_LINEAR_CLAMP];
3796 break;
3797 case SDL_TEXTURE_ADDRESS_WRAP:
3798 textureSampler = rendererData->samplers[VULKAN_SAMPLER_LINEAR_WRAP];
3799 break;
3800 default:
3801 return SDL_SetError("Unknown texture address mode: %d", cmd->data.draw.texture_address_mode);
3802 }
3803 break;
3804 default:
3805 return SDL_SetError("Unknown scale mode: %d", cmd->data.draw.texture_scale_mode);
3806 }
3807
3808 if (textureData->mainImage.imageLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
3809 bool stoppedRenderPass = false;
3810 if (rendererData->currentRenderPass != VK_NULL_HANDLE) {
3811 vkCmdEndRenderPass(rendererData->currentCommandBuffer);
3812 rendererData->currentRenderPass = VK_NULL_HANDLE;
3813 stoppedRenderPass = true;
3814 }
3815
3816 VULKAN_RecordPipelineImageBarrier(rendererData,
3817 VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
3818 VK_ACCESS_SHADER_READ_BIT,
3819 VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
3820 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
3821 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
3822 textureData->mainImage.image,
3823 &textureData->mainImage.imageLayout);
3824
3825 if (stoppedRenderPass) {
3826 VULKAN_BeginRenderPass(rendererData, VK_ATTACHMENT_LOAD_OP_LOAD, NULL);
3827 }
3828 }
3829
3830 return VULKAN_SetDrawState(renderer, cmd, textureData->shader, pipelineLayout, descriptorSetLayout, &constants, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, textureData->mainImage.imageView, textureSampler, matrix, stateCache);
3831}
3832
3833static void VULKAN_DrawPrimitives(SDL_Renderer *renderer, VkPrimitiveTopology primitiveTopology, const size_t vertexStart, const size_t vertexCount)
3834{
3835 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
3836 vkCmdDraw(rendererData->currentCommandBuffer, (uint32_t)vertexCount, 1, (uint32_t)vertexStart, 0);
3837}
3838
3839static void VULKAN_InvalidateCachedState(SDL_Renderer *renderer)
3840{
3841 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
3842 rendererData->currentPipelineState = NULL;
3843 rendererData->cliprectDirty = true;
3844}
3845
3846static bool VULKAN_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
3847{
3848 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
3849 VkSurfaceTransformFlagBitsKHR currentRotation = VULKAN_GetRotationForCurrentRenderTarget(rendererData);
3850 VULKAN_DrawStateCache stateCache;
3851 SDL_memset(&stateCache, 0, sizeof(stateCache));
3852
3853 if (!rendererData->device) {
3854 return SDL_SetError("Device lost and couldn't be recovered");
3855 }
3856
3857 if(rendererData->currentViewportRotation != currentRotation) {
3858 rendererData->currentViewportRotation = currentRotation;
3859 rendererData->viewportDirty = true;
3860 rendererData->cliprectDirty = true;
3861 }
3862
3863 if (rendererData->recreateSwapchain) {
3864 if (VULKAN_UpdateForWindowSizeChange(renderer) != VK_SUCCESS) {
3865 return false;
3866 }
3867 rendererData->recreateSwapchain = false;
3868 }
3869
3870 if (!VULKAN_UpdateVertexBuffer(renderer, vertices, vertsize, &stateCache)) {
3871 return false;
3872 }
3873
3874 while (cmd) {
3875 switch (cmd->command) {
3876 case SDL_RENDERCMD_SETDRAWCOLOR:
3877 {
3878 break; // this isn't currently used in this render backend.
3879 }
3880
3881 case SDL_RENDERCMD_SETVIEWPORT:
3882 {
3883 SDL_Rect *viewport = &rendererData->currentViewport;
3884 if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof(cmd->data.viewport.rect)) != 0) {
3885 SDL_copyp(viewport, &cmd->data.viewport.rect);
3886 rendererData->viewportDirty = true;
3887 rendererData->cliprectDirty = true;
3888 }
3889 break;
3890 }
3891
3892 case SDL_RENDERCMD_SETCLIPRECT:
3893 {
3894 const SDL_Rect *rect = &cmd->data.cliprect.rect;
3895 if (rendererData->currentCliprectEnabled != cmd->data.cliprect.enabled) {
3896 rendererData->currentCliprectEnabled = cmd->data.cliprect.enabled;
3897 rendererData->cliprectDirty = true;
3898 }
3899 if (SDL_memcmp(&rendererData->currentCliprect, rect, sizeof(*rect)) != 0) {
3900 SDL_copyp(&rendererData->currentCliprect, rect);
3901 rendererData->cliprectDirty = true;
3902 }
3903 break;
3904 }
3905
3906 case SDL_RENDERCMD_CLEAR:
3907 {
3908 bool convert_color = SDL_RenderingLinearSpace(renderer);
3909 SDL_FColor color = cmd->data.color.color;
3910 if (convert_color) {
3911 SDL_ConvertToLinear(&color);
3912 }
3913 color.r *= cmd->data.color.color_scale;
3914 color.g *= cmd->data.color.color_scale;
3915 color.b *= cmd->data.color.color_scale;
3916
3917 VkClearColorValue clearColor;
3918 clearColor.float32[0] = color.r;
3919 clearColor.float32[1] = color.g;
3920 clearColor.float32[2] = color.b;
3921 clearColor.float32[3] = color.a;
3922 VULKAN_ActivateCommandBuffer(renderer, VK_ATTACHMENT_LOAD_OP_CLEAR, &clearColor, &stateCache);
3923 break;
3924 }
3925
3926 case SDL_RENDERCMD_DRAW_POINTS:
3927 {
3928 const size_t count = cmd->data.draw.count;
3929 const size_t first = cmd->data.draw.first;
3930 const size_t start = first / sizeof(VULKAN_VertexPositionColor);
3931 VULKAN_SetDrawState(renderer, cmd, SHADER_SOLID, rendererData->pipelineLayout, rendererData->descriptorSetLayout, NULL, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, VK_NULL_HANDLE, VK_NULL_HANDLE, NULL, &stateCache);
3932 VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, start, count);
3933 break;
3934 }
3935
3936 case SDL_RENDERCMD_DRAW_LINES:
3937 {
3938 const size_t count = cmd->data.draw.count;
3939 const size_t first = cmd->data.draw.first;
3940 const size_t start = first / sizeof(VULKAN_VertexPositionColor);
3941 const VULKAN_VertexPositionColor *verts = (VULKAN_VertexPositionColor *)(((Uint8 *)vertices) + first);
3942 VULKAN_SetDrawState(renderer, cmd, SHADER_SOLID, rendererData->pipelineLayout, rendererData->descriptorSetLayout, NULL, VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, VK_NULL_HANDLE, VK_NULL_HANDLE, NULL, &stateCache);
3943 VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, start, count);
3944 if (verts[0].pos[0] != verts[count - 1].pos[0] || verts[0].pos[1] != verts[count - 1].pos[1]) {
3945 VULKAN_SetDrawState(renderer, cmd, SHADER_SOLID, rendererData->pipelineLayout, rendererData->descriptorSetLayout, NULL, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, VK_NULL_HANDLE, VK_NULL_HANDLE, NULL, &stateCache);
3946 VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, start + (count - 1), 1);
3947 }
3948 break;
3949 }
3950
3951 case SDL_RENDERCMD_FILL_RECTS: // unused
3952 break;
3953
3954 case SDL_RENDERCMD_COPY: // unused
3955 break;
3956
3957 case SDL_RENDERCMD_COPY_EX: // unused
3958 break;
3959
3960 case SDL_RENDERCMD_GEOMETRY:
3961 {
3962 SDL_Texture *texture = cmd->data.draw.texture;
3963 const size_t count = cmd->data.draw.count;
3964 const size_t first = cmd->data.draw.first;
3965 const size_t start = first / sizeof(VULKAN_VertexPositionColor);
3966
3967 if (texture) {
3968 VULKAN_SetCopyState(renderer, cmd, NULL, &stateCache);
3969 } else {
3970 VULKAN_SetDrawState(renderer, cmd, SHADER_SOLID, rendererData->pipelineLayout, rendererData->descriptorSetLayout, NULL, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, VK_NULL_HANDLE, VK_NULL_HANDLE, NULL, &stateCache);
3971 }
3972
3973 VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, start, count);
3974 break;
3975 }
3976
3977 case SDL_RENDERCMD_NO_OP:
3978 break;
3979 }
3980
3981 cmd = cmd->next;
3982 }
3983 return true;
3984}
3985
3986static SDL_Surface* VULKAN_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect)
3987{
3988 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
3989 VkImage backBuffer;
3990 VkImageLayout *imageLayout;
3991 VULKAN_Buffer readbackBuffer;
3992 VkDeviceSize pixelSize;
3993 VkDeviceSize length;
3994 VkDeviceSize readbackBufferSize;
3995 VkFormat vkFormat;
3996 SDL_Surface *output;
3997
3998 VULKAN_EnsureCommandBuffer(rendererData);
3999
4000 // Stop any outstanding renderpass if open
4001 if (rendererData->currentRenderPass != VK_NULL_HANDLE) {
4002 vkCmdEndRenderPass(rendererData->currentCommandBuffer);
4003 rendererData->currentRenderPass = VK_NULL_HANDLE;
4004 }
4005
4006 if (rendererData->textureRenderTarget) {
4007 backBuffer = rendererData->textureRenderTarget->mainImage.image;
4008 imageLayout = &rendererData->textureRenderTarget->mainImage.imageLayout;
4009 vkFormat = rendererData->textureRenderTarget->mainImage.format;
4010 } else {
4011 backBuffer = rendererData->swapchainImages[rendererData->currentSwapchainImageIndex];
4012 imageLayout = &rendererData->swapchainImageLayouts[rendererData->currentSwapchainImageIndex];
4013 vkFormat = rendererData->surfaceFormat.format;
4014 }
4015
4016 pixelSize = VULKAN_GetBytesPerPixel(vkFormat);
4017 length = rect->w * pixelSize;
4018 readbackBufferSize = length * rect->h;
4019 if (VULKAN_AllocateBuffer(rendererData, readbackBufferSize,
4020 VK_BUFFER_USAGE_TRANSFER_DST_BIT,
4021 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
4022 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
4023 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
4024 &readbackBuffer) != VK_SUCCESS) {
4025 return NULL;
4026 }
4027
4028
4029 // Make sure the source is in the correct resource state
4030 VULKAN_RecordPipelineImageBarrier(rendererData,
4031 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
4032 VK_ACCESS_TRANSFER_READ_BIT,
4033 VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
4034 VK_PIPELINE_STAGE_TRANSFER_BIT,
4035 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
4036 backBuffer,
4037 imageLayout);
4038
4039 // Copy the image to the readback buffer
4040 VkBufferImageCopy region;
4041 region.bufferOffset = 0;
4042 region.bufferRowLength = 0;
4043 region.bufferImageHeight = 0;
4044 region.imageSubresource.baseArrayLayer = 0;
4045 region.imageSubresource.layerCount = 1;
4046 region.imageSubresource.mipLevel = 0;
4047 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
4048 region.imageOffset.x = rect->x;
4049 region.imageOffset.y = rect->y;
4050 region.imageOffset.z = 0;
4051 region.imageExtent.width = rect->w;
4052 region.imageExtent.height = rect->h;
4053 region.imageExtent.depth = 1;
4054 vkCmdCopyImageToBuffer(rendererData->currentCommandBuffer, backBuffer, *imageLayout, readbackBuffer.buffer, 1, &region);
4055
4056 // We need to issue the command list for the copy to finish
4057 VULKAN_IssueBatch(rendererData);
4058
4059 // Transition the render target back to a render target
4060 VULKAN_RecordPipelineImageBarrier(rendererData,
4061 VK_ACCESS_TRANSFER_WRITE_BIT,
4062 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
4063 VK_PIPELINE_STAGE_TRANSFER_BIT,
4064 VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
4065 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
4066 backBuffer,
4067 imageLayout);
4068
4069 output = SDL_DuplicatePixels(
4070 rect->w, rect->h,
4071 VULKAN_VkFormatToSDLPixelFormat(vkFormat),
4072 renderer->target ? renderer->target->colorspace : renderer->output_colorspace,
4073 readbackBuffer.mappedBufferPtr,
4074 (int)length);
4075
4076 VULKAN_DestroyBuffer(rendererData, &readbackBuffer);
4077
4078 return output;
4079}
4080
4081static bool VULKAN_AddVulkanRenderSemaphores(SDL_Renderer *renderer, Uint32 wait_stage_mask, Sint64 wait_semaphore, Sint64 signal_semaphore)
4082{
4083 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
4084
4085 if (wait_semaphore) {
4086 if (rendererData->waitRenderSemaphoreCount == rendererData->waitRenderSemaphoreMax) {
4087 // Allocate an additional one at the end for the normal present wait
4088 VkPipelineStageFlags *waitDestStageMasks = (VkPipelineStageFlags *)SDL_realloc(rendererData->waitDestStageMasks, (rendererData->waitRenderSemaphoreMax + 2) * sizeof(*waitDestStageMasks));
4089 if (!waitDestStageMasks) {
4090 return false;
4091 }
4092 rendererData->waitDestStageMasks = waitDestStageMasks;
4093
4094 VkSemaphore *semaphores = (VkSemaphore *)SDL_realloc(rendererData->waitRenderSemaphores, (rendererData->waitRenderSemaphoreMax + 2) * sizeof(*semaphores));
4095 if (!semaphores) {
4096 return false;
4097 }
4098 rendererData->waitRenderSemaphores = semaphores;
4099 ++rendererData->waitRenderSemaphoreMax;
4100 }
4101 rendererData->waitDestStageMasks[rendererData->waitRenderSemaphoreCount] = wait_stage_mask;
4102 rendererData->waitRenderSemaphores[rendererData->waitRenderSemaphoreCount] = (VkSemaphore)wait_semaphore;
4103 ++rendererData->waitRenderSemaphoreCount;
4104 }
4105
4106 if (signal_semaphore) {
4107 if (rendererData->signalRenderSemaphoreCount == rendererData->signalRenderSemaphoreMax) {
4108 // Allocate an additional one at the end for the normal present signal
4109 VkSemaphore *semaphores = (VkSemaphore *)SDL_realloc(rendererData->signalRenderSemaphores, (rendererData->signalRenderSemaphoreMax + 2) * sizeof(*semaphores));
4110 if (!semaphores) {
4111 return false;
4112 }
4113 rendererData->signalRenderSemaphores = semaphores;
4114 ++rendererData->signalRenderSemaphoreMax;
4115 }
4116 rendererData->signalRenderSemaphores[rendererData->signalRenderSemaphoreCount] = (VkSemaphore)signal_semaphore;
4117 ++rendererData->signalRenderSemaphoreCount;
4118 }
4119
4120 return true;
4121}
4122
4123static bool VULKAN_RenderPresent(SDL_Renderer *renderer)
4124{
4125 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
4126 VkResult result = VK_SUCCESS;
4127
4128 if (!rendererData->device) {
4129 return SDL_SetError("Device lost and couldn't be recovered");
4130 }
4131
4132 if (rendererData->currentCommandBuffer) {
4133 rendererData->currentPipelineState = VK_NULL_HANDLE;
4134 rendererData->viewportDirty = true;
4135
4136 VULKAN_RecordPipelineImageBarrier(rendererData,
4137 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
4138 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
4139 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
4140 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
4141 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
4142 rendererData->swapchainImages[rendererData->currentSwapchainImageIndex],
4143 &rendererData->swapchainImageLayouts[rendererData->currentSwapchainImageIndex]);
4144
4145 vkEndCommandBuffer(rendererData->currentCommandBuffer);
4146
4147 result = vkResetFences(rendererData->device, 1, &rendererData->fences[rendererData->currentCommandBufferIndex]);
4148 if (result != VK_SUCCESS) {
4149 SET_ERROR_CODE("vkResetFences()", result);
4150 return false;
4151 }
4152
4153 VkPipelineStageFlags waitDestStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
4154 VkSubmitInfo submitInfo = { 0 };
4155 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
4156 if (rendererData->waitRenderSemaphoreCount > 0) {
4157 Uint32 additionalSemaphoreCount = (rendererData->currentImageAvailableSemaphore != VK_NULL_HANDLE) ? 1 : 0;
4158 submitInfo.waitSemaphoreCount = rendererData->waitRenderSemaphoreCount + additionalSemaphoreCount;
4159 if (additionalSemaphoreCount > 0) {
4160 rendererData->waitRenderSemaphores[rendererData->waitRenderSemaphoreCount] = rendererData->currentImageAvailableSemaphore;
4161 rendererData->waitDestStageMasks[rendererData->waitRenderSemaphoreCount] = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
4162 }
4163 submitInfo.pWaitSemaphores = rendererData->waitRenderSemaphores;
4164 submitInfo.pWaitDstStageMask = rendererData->waitDestStageMasks;
4165 rendererData->waitRenderSemaphoreCount = 0;
4166 } else if (rendererData->currentImageAvailableSemaphore != VK_NULL_HANDLE) {
4167 submitInfo.waitSemaphoreCount = 1;
4168 submitInfo.pWaitSemaphores = &rendererData->currentImageAvailableSemaphore;
4169 submitInfo.pWaitDstStageMask = &waitDestStageMask;
4170 }
4171 submitInfo.commandBufferCount = 1;
4172 submitInfo.pCommandBuffers = &rendererData->currentCommandBuffer;
4173 if (rendererData->signalRenderSemaphoreCount > 0) {
4174 submitInfo.signalSemaphoreCount = rendererData->signalRenderSemaphoreCount + 1;
4175 rendererData->signalRenderSemaphores[rendererData->signalRenderSemaphoreCount] = rendererData->renderingFinishedSemaphores[rendererData->currentCommandBufferIndex];
4176 submitInfo.pSignalSemaphores = rendererData->signalRenderSemaphores;
4177 rendererData->signalRenderSemaphoreCount = 0;
4178 } else {
4179 submitInfo.signalSemaphoreCount = 1;
4180 submitInfo.pSignalSemaphores = &rendererData->renderingFinishedSemaphores[rendererData->currentCommandBufferIndex];
4181 }
4182 result = vkQueueSubmit(rendererData->graphicsQueue, 1, &submitInfo, rendererData->fences[rendererData->currentCommandBufferIndex]);
4183 if (result != VK_SUCCESS) {
4184 if (result == VK_ERROR_DEVICE_LOST) {
4185 if (VULKAN_HandleDeviceLost(renderer)) {
4186 SDL_SetError("Present failed, device lost");
4187 } else {
4188 // Recovering from device lost failed, error is already set
4189 }
4190 } else {
4191 SET_ERROR_CODE("vkQueueSubmit()", result);
4192 }
4193 return false;
4194 }
4195 rendererData->currentCommandBuffer = VK_NULL_HANDLE;
4196 rendererData->currentImageAvailableSemaphore = VK_NULL_HANDLE;
4197
4198 VkPresentInfoKHR presentInfo = { 0 };
4199 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
4200 presentInfo.waitSemaphoreCount = 1;
4201 presentInfo.pWaitSemaphores = &rendererData->renderingFinishedSemaphores[rendererData->currentCommandBufferIndex];
4202 presentInfo.swapchainCount = 1;
4203 presentInfo.pSwapchains = &rendererData->swapchain;
4204 presentInfo.pImageIndices = &rendererData->currentSwapchainImageIndex;
4205 result = vkQueuePresentKHR(rendererData->presentQueue, &presentInfo);
4206 if ((result != VK_SUCCESS) && (result != VK_ERROR_OUT_OF_DATE_KHR) && (result != VK_ERROR_SURFACE_LOST_KHR) && (result != VK_SUBOPTIMAL_KHR )) {
4207 SET_ERROR_CODE("vkQueuePresentKHR()", result);
4208 return false;
4209 }
4210
4211 rendererData->currentCommandBufferIndex = ( rendererData->currentCommandBufferIndex + 1 ) % rendererData->swapchainImageCount;
4212
4213 // Wait for previous time this command buffer was submitted, will be N frames ago
4214 result = vkWaitForFences(rendererData->device, 1, &rendererData->fences[rendererData->currentCommandBufferIndex], VK_TRUE, UINT64_MAX);
4215 if (result != VK_SUCCESS) {
4216 if (result == VK_ERROR_DEVICE_LOST) {
4217 if (VULKAN_HandleDeviceLost(renderer)) {
4218 SDL_SetError("Present failed, device lost");
4219 } else {
4220 // Recovering from device lost failed, error is already set
4221 }
4222 } else {
4223 SET_ERROR_CODE("vkWaitForFences()", result);
4224 }
4225 return false;
4226 }
4227
4228 VULKAN_AcquireNextSwapchainImage(renderer);
4229 }
4230
4231 return true;
4232}
4233
4234static bool VULKAN_SetVSync(SDL_Renderer *renderer, const int vsync)
4235{
4236 VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
4237
4238 switch (vsync) {
4239 case -1:
4240 case 0:
4241 case 1:
4242 // Supported
4243 break;
4244 default:
4245 return SDL_Unsupported();
4246 }
4247 if (vsync != rendererData->vsync) {
4248 rendererData->vsync = vsync;
4249 rendererData->recreateSwapchain = true;
4250 }
4251 return true;
4252}
4253
4254static bool VULKAN_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_PropertiesID create_props)
4255{
4256 VULKAN_RenderData *rendererData;
4257
4258 SDL_SetupRendererColorspace(renderer, create_props);
4259
4260 if (renderer->output_colorspace != SDL_COLORSPACE_SRGB &&
4261 renderer->output_colorspace != SDL_COLORSPACE_SRGB_LINEAR
4262 /*&& renderer->output_colorspace != SDL_COLORSPACE_HDR10*/) {
4263 return SDL_SetError("Unsupported output colorspace");
4264 }
4265
4266 rendererData = (VULKAN_RenderData *)SDL_calloc(1, sizeof(*rendererData));
4267 if (!rendererData) {
4268 return false;
4269 }
4270
4271 rendererData->identity = MatrixIdentity();
4272 rendererData->identitySwizzle.r = VK_COMPONENT_SWIZZLE_IDENTITY;
4273 rendererData->identitySwizzle.g = VK_COMPONENT_SWIZZLE_IDENTITY;
4274 rendererData->identitySwizzle.b = VK_COMPONENT_SWIZZLE_IDENTITY;
4275 rendererData->identitySwizzle.a = VK_COMPONENT_SWIZZLE_IDENTITY;
4276
4277 // Save the create props in case we need to recreate on device lost
4278 rendererData->create_props = SDL_CreateProperties();
4279 if (!SDL_CopyProperties(create_props, rendererData->create_props)) {
4280 SDL_free(rendererData);
4281 return false;
4282 }
4283
4284 renderer->WindowEvent = VULKAN_WindowEvent;
4285 renderer->SupportsBlendMode = VULKAN_SupportsBlendMode;
4286 renderer->CreateTexture = VULKAN_CreateTexture;
4287 renderer->UpdateTexture = VULKAN_UpdateTexture;
4288#ifdef SDL_HAVE_YUV
4289 renderer->UpdateTextureYUV = VULKAN_UpdateTextureYUV;
4290 renderer->UpdateTextureNV = VULKAN_UpdateTextureNV;
4291#endif
4292 renderer->LockTexture = VULKAN_LockTexture;
4293 renderer->UnlockTexture = VULKAN_UnlockTexture;
4294 renderer->SetRenderTarget = VULKAN_SetRenderTarget;
4295 renderer->QueueSetViewport = VULKAN_QueueNoOp;
4296 renderer->QueueSetDrawColor = VULKAN_QueueNoOp;
4297 renderer->QueueDrawPoints = VULKAN_QueueDrawPoints;
4298 renderer->QueueDrawLines = VULKAN_QueueDrawPoints; // lines and points queue vertices the same way.
4299 renderer->QueueGeometry = VULKAN_QueueGeometry;
4300 renderer->InvalidateCachedState = VULKAN_InvalidateCachedState;
4301 renderer->RunCommandQueue = VULKAN_RunCommandQueue;
4302 renderer->RenderReadPixels = VULKAN_RenderReadPixels;
4303 renderer->AddVulkanRenderSemaphores = VULKAN_AddVulkanRenderSemaphores;
4304 renderer->RenderPresent = VULKAN_RenderPresent;
4305 renderer->DestroyTexture = VULKAN_DestroyTexture;
4306 renderer->DestroyRenderer = VULKAN_DestroyRenderer;
4307 renderer->SetVSync = VULKAN_SetVSync;
4308 renderer->internal = rendererData;
4309 VULKAN_InvalidateCachedState(renderer);
4310
4311 renderer->name = VULKAN_RenderDriver.name;
4312 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ARGB8888);
4313 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR8888);
4314 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR2101010);
4315 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_RGBA64_FLOAT);
4316 SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 16384);
4317
4318 /* HACK: make sure the SDL_Renderer references the SDL_Window data now, in
4319 * order to give init functions access to the underlying window handle:
4320 */
4321 renderer->window = window;
4322
4323 // Initialize Vulkan resources
4324 if (VULKAN_CreateDeviceResources(renderer, create_props) != VK_SUCCESS) {
4325 return false;
4326 }
4327
4328 if (VULKAN_CreateWindowSizeDependentResources(renderer) != VK_SUCCESS) {
4329 return false;
4330 }
4331
4332#ifdef SDL_HAVE_YUV
4333 if (rendererData->supportsKHRSamplerYCbCrConversion) {
4334 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_YV12);
4335 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_IYUV);
4336 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV12);
4337 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_NV21);
4338 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_P010);
4339 }
4340#endif
4341
4342 return true;
4343}
4344
4345SDL_RenderDriver VULKAN_RenderDriver = {
4346 VULKAN_CreateRenderer, "vulkan"
4347};
4348
4349#endif // SDL_VIDEO_RENDER_VULKAN
4350