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