summaryrefslogtreecommitdiff
path: root/src/contrib/SDL-3.2.20/test/testffmpeg_vulkan.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/contrib/SDL-3.2.20/test/testffmpeg_vulkan.c')
-rw-r--r--src/contrib/SDL-3.2.20/test/testffmpeg_vulkan.c1018
1 files changed, 1018 insertions, 0 deletions
diff --git a/src/contrib/SDL-3.2.20/test/testffmpeg_vulkan.c b/src/contrib/SDL-3.2.20/test/testffmpeg_vulkan.c
new file mode 100644
index 0000000..a6da6cb
--- /dev/null
+++ b/src/contrib/SDL-3.2.20/test/testffmpeg_vulkan.c
@@ -0,0 +1,1018 @@
1/*
2 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely.
11*/
12#include <SDL3/SDL.h>
13#include <SDL3/SDL_vulkan.h>
14
15#include "testffmpeg_vulkan.h"
16
17#ifdef FFMPEG_VULKAN_SUPPORT
18
19#define VULKAN_FUNCTIONS() \
20 VULKAN_GLOBAL_FUNCTION(vkCreateInstance) \
21 VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceExtensionProperties) \
22 VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceLayerProperties) \
23 VULKAN_INSTANCE_FUNCTION(vkCreateDevice) \
24 VULKAN_INSTANCE_FUNCTION(vkDestroyInstance) \
25 VULKAN_INSTANCE_FUNCTION(vkDestroySurfaceKHR) \
26 VULKAN_INSTANCE_FUNCTION(vkEnumerateDeviceExtensionProperties) \
27 VULKAN_INSTANCE_FUNCTION(vkEnumeratePhysicalDevices) \
28 VULKAN_INSTANCE_FUNCTION(vkGetDeviceProcAddr) \
29 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures2) \
30 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties) \
31 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR) \
32 VULKAN_INSTANCE_FUNCTION(vkQueueWaitIdle) \
33 VULKAN_DEVICE_FUNCTION(vkAllocateCommandBuffers) \
34 VULKAN_DEVICE_FUNCTION(vkBeginCommandBuffer) \
35 VULKAN_DEVICE_FUNCTION(vkCmdPipelineBarrier2) \
36 VULKAN_DEVICE_FUNCTION(vkCreateCommandPool) \
37 VULKAN_DEVICE_FUNCTION(vkCreateSemaphore) \
38 VULKAN_DEVICE_FUNCTION(vkDestroyCommandPool) \
39 VULKAN_DEVICE_FUNCTION(vkDestroyDevice) \
40 VULKAN_DEVICE_FUNCTION(vkDestroySemaphore) \
41 VULKAN_DEVICE_FUNCTION(vkDeviceWaitIdle) \
42 VULKAN_DEVICE_FUNCTION(vkEndCommandBuffer) \
43 VULKAN_DEVICE_FUNCTION(vkFreeCommandBuffers) \
44 VULKAN_DEVICE_FUNCTION(vkGetDeviceQueue) \
45 VULKAN_DEVICE_FUNCTION(vkQueueSubmit) \
46\
47VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceVideoFormatPropertiesKHR) \
48
49typedef struct
50{
51 VkPhysicalDeviceFeatures2 device_features;
52 VkPhysicalDeviceVulkan11Features device_features_1_1;
53 VkPhysicalDeviceVulkan12Features device_features_1_2;
54 VkPhysicalDeviceVulkan13Features device_features_1_3;
55 VkPhysicalDeviceDescriptorBufferFeaturesEXT desc_buf_features;
56 VkPhysicalDeviceShaderAtomicFloatFeaturesEXT atomic_float_features;
57 VkPhysicalDeviceCooperativeMatrixFeaturesKHR coop_matrix_features;
58} VulkanDeviceFeatures;
59
60struct VulkanVideoContext
61{
62 VkInstance instance;
63 VkSurfaceKHR surface;
64 VkPhysicalDevice physicalDevice;
65 int presentQueueFamilyIndex;
66 int presentQueueCount;
67 int graphicsQueueFamilyIndex;
68 int graphicsQueueCount;
69 int transferQueueFamilyIndex;
70 int transferQueueCount;
71 int computeQueueFamilyIndex;
72 int computeQueueCount;
73 int decodeQueueFamilyIndex;
74 int decodeQueueCount;
75 VkDevice device;
76 VkQueue graphicsQueue;
77 VkCommandPool commandPool;
78 VkCommandBuffer *commandBuffers;
79 uint32_t commandBufferCount;
80 uint32_t commandBufferIndex;
81 VkSemaphore *waitSemaphores;
82 uint32_t waitSemaphoreCount;
83 VkSemaphore *signalSemaphores;
84 uint32_t signalSemaphoreCount;
85
86 const char **instanceExtensions;
87 int instanceExtensionsCount;
88
89 const char **deviceExtensions;
90 int deviceExtensionsCount;
91
92 VulkanDeviceFeatures features;
93
94 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
95#define VULKAN_GLOBAL_FUNCTION(name) PFN_##name name;
96#define VULKAN_INSTANCE_FUNCTION(name) PFN_##name name;
97#define VULKAN_DEVICE_FUNCTION(name) PFN_##name name;
98 VULKAN_FUNCTIONS()
99#undef VULKAN_GLOBAL_FUNCTION
100#undef VULKAN_INSTANCE_FUNCTION
101#undef VULKAN_DEVICE_FUNCTION
102};
103
104
105static int loadGlobalFunctions(VulkanVideoContext *context)
106{
107 context->vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr();
108 if (!context->vkGetInstanceProcAddr) {
109 return -1;
110 }
111
112#define VULKAN_GLOBAL_FUNCTION(name) \
113 context->name = (PFN_##name)context->vkGetInstanceProcAddr(VK_NULL_HANDLE, #name); \
114 if (!context->name) { \
115 return SDL_SetError("vkGetInstanceProcAddr(VK_NULL_HANDLE, \"" #name "\") failed"); \
116 }
117#define VULKAN_INSTANCE_FUNCTION(name)
118#define VULKAN_DEVICE_FUNCTION(name)
119 VULKAN_FUNCTIONS()
120#undef VULKAN_GLOBAL_FUNCTION
121#undef VULKAN_INSTANCE_FUNCTION
122#undef VULKAN_DEVICE_FUNCTION
123 return 0;
124}
125
126static int loadInstanceFunctions(VulkanVideoContext *context)
127{
128#define VULKAN_GLOBAL_FUNCTION(name)
129#define VULKAN_INSTANCE_FUNCTION(name) \
130 context->name = (PFN_##name)context->vkGetInstanceProcAddr(context->instance, #name); \
131 if (!context->name) { \
132 return SDL_SetError("vkGetInstanceProcAddr(instance, \"" #name "\") failed"); \
133 }
134#define VULKAN_DEVICE_FUNCTION(name)
135 VULKAN_FUNCTIONS()
136#undef VULKAN_GLOBAL_FUNCTION
137#undef VULKAN_INSTANCE_FUNCTION
138#undef VULKAN_DEVICE_FUNCTION
139 return 0;
140}
141
142static int loadDeviceFunctions(VulkanVideoContext *context)
143{
144#define VULKAN_GLOBAL_FUNCTION(name)
145#define VULKAN_INSTANCE_FUNCTION(name)
146#define VULKAN_DEVICE_FUNCTION(name) \
147 context->name = (PFN_##name)context->vkGetDeviceProcAddr(context->device, #name); \
148 if (!context->name) { \
149 return SDL_SetError("vkGetDeviceProcAddr(device, \"" #name "\") failed"); \
150 }
151 VULKAN_FUNCTIONS()
152#undef VULKAN_GLOBAL_FUNCTION
153#undef VULKAN_INSTANCE_FUNCTION
154#undef VULKAN_DEVICE_FUNCTION
155 return 0;
156}
157
158#undef VULKAN_FUNCTIONS
159
160static const char *getVulkanResultString(VkResult result)
161{
162 switch ((int)result) {
163#define RESULT_CASE(x) \
164 case x: \
165 return #x
166 RESULT_CASE(VK_SUCCESS);
167 RESULT_CASE(VK_NOT_READY);
168 RESULT_CASE(VK_TIMEOUT);
169 RESULT_CASE(VK_EVENT_SET);
170 RESULT_CASE(VK_EVENT_RESET);
171 RESULT_CASE(VK_INCOMPLETE);
172 RESULT_CASE(VK_ERROR_OUT_OF_HOST_MEMORY);
173 RESULT_CASE(VK_ERROR_OUT_OF_DEVICE_MEMORY);
174 RESULT_CASE(VK_ERROR_INITIALIZATION_FAILED);
175 RESULT_CASE(VK_ERROR_DEVICE_LOST);
176 RESULT_CASE(VK_ERROR_MEMORY_MAP_FAILED);
177 RESULT_CASE(VK_ERROR_LAYER_NOT_PRESENT);
178 RESULT_CASE(VK_ERROR_EXTENSION_NOT_PRESENT);
179 RESULT_CASE(VK_ERROR_FEATURE_NOT_PRESENT);
180 RESULT_CASE(VK_ERROR_INCOMPATIBLE_DRIVER);
181 RESULT_CASE(VK_ERROR_TOO_MANY_OBJECTS);
182 RESULT_CASE(VK_ERROR_FORMAT_NOT_SUPPORTED);
183 RESULT_CASE(VK_ERROR_FRAGMENTED_POOL);
184 RESULT_CASE(VK_ERROR_SURFACE_LOST_KHR);
185 RESULT_CASE(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR);
186 RESULT_CASE(VK_SUBOPTIMAL_KHR);
187 RESULT_CASE(VK_ERROR_OUT_OF_DATE_KHR);
188 RESULT_CASE(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR);
189 RESULT_CASE(VK_ERROR_VALIDATION_FAILED_EXT);
190 RESULT_CASE(VK_ERROR_OUT_OF_POOL_MEMORY_KHR);
191 RESULT_CASE(VK_ERROR_INVALID_SHADER_NV);
192#undef RESULT_CASE
193 default:
194 break;
195 }
196 return (result < 0) ? "VK_ERROR_<Unknown>" : "VK_<Unknown>";
197}
198
199static int createInstance(VulkanVideoContext *context)
200{
201 static const char *optional_extensions[] = {
202 VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME,
203 VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME
204 };
205 VkApplicationInfo appInfo = { 0 };
206 VkInstanceCreateInfo instanceCreateInfo = { 0 };
207 VkResult result;
208 char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount);
209
210 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
211 appInfo.apiVersion = VK_API_VERSION_1_3;
212 instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
213 instanceCreateInfo.pApplicationInfo = &appInfo;
214
215 const char **instanceExtensionsCopy = SDL_calloc(instanceCreateInfo.enabledExtensionCount + SDL_arraysize(optional_extensions), sizeof(const char *));
216 for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) {
217 instanceExtensionsCopy[i] = instanceExtensions[i];
218 }
219
220 // Get the rest of the optional extensions
221 {
222 uint32_t extensionCount;
223 if (context->vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, NULL) == VK_SUCCESS && extensionCount > 0) {
224 VkExtensionProperties *extensionProperties = SDL_calloc(extensionCount, sizeof(VkExtensionProperties));
225 if (context->vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensionProperties) == VK_SUCCESS) {
226 for (uint32_t i = 0; i < SDL_arraysize(optional_extensions); ++i) {
227 for (uint32_t j = 0; j < extensionCount; ++j) {
228 if (SDL_strcmp(extensionProperties[j].extensionName, optional_extensions[i]) == 0) {
229 instanceExtensionsCopy[instanceCreateInfo.enabledExtensionCount++] = optional_extensions[i];
230 break;
231 }
232 }
233 }
234 }
235 SDL_free(extensionProperties);
236 }
237 }
238 instanceCreateInfo.ppEnabledExtensionNames = instanceExtensionsCopy;
239
240 context->instanceExtensions = instanceExtensionsCopy;
241 context->instanceExtensionsCount = instanceCreateInfo.enabledExtensionCount;
242
243 result = context->vkCreateInstance(&instanceCreateInfo, NULL, &context->instance);
244 if (result != VK_SUCCESS) {
245 context->instance = VK_NULL_HANDLE;
246 return SDL_SetError("vkCreateInstance(): %s", getVulkanResultString(result));
247 }
248 if (loadInstanceFunctions(context) < 0) {
249 return -1;
250 }
251 return 0;
252}
253
254static int createSurface(VulkanVideoContext *context, SDL_Window *window)
255{
256 if (!SDL_Vulkan_CreateSurface(window, context->instance, NULL, &context->surface)) {
257 context->surface = VK_NULL_HANDLE;
258 return -1;
259 }
260 return 0;
261}
262
263// Use the same queue scoring algorithm as ffmpeg to make sure we get the same device configuration
264static int selectQueueFamily(VkQueueFamilyProperties *queueFamiliesProperties, uint32_t queueFamiliesCount, VkQueueFlagBits flags, int *queueCount)
265{
266 uint32_t queueFamilyIndex;
267 uint32_t selectedQueueFamilyIndex = queueFamiliesCount;
268 uint32_t min_score = ~0u;
269
270 for (queueFamilyIndex = 0; queueFamilyIndex < queueFamiliesCount; ++queueFamilyIndex) {
271 VkQueueFlagBits current_flags = queueFamiliesProperties[queueFamilyIndex].queueFlags;
272 if (current_flags & flags) {
273 uint32_t score = av_popcount(current_flags) + queueFamiliesProperties[queueFamilyIndex].timestampValidBits;
274 if (score < min_score) {
275 selectedQueueFamilyIndex = queueFamilyIndex;
276 min_score = score;
277 }
278 }
279 }
280
281 if (selectedQueueFamilyIndex != queueFamiliesCount) {
282 VkQueueFamilyProperties *selectedQueueFamily = &queueFamiliesProperties[selectedQueueFamilyIndex];
283 *queueCount = (int)selectedQueueFamily->queueCount;
284 ++selectedQueueFamily->timestampValidBits;
285 return (int)selectedQueueFamilyIndex;
286 } else {
287 *queueCount = 0;
288 return -1;
289 }
290}
291
292static int findPhysicalDevice(VulkanVideoContext *context)
293{
294 uint32_t physicalDeviceCount = 0;
295 VkPhysicalDevice *physicalDevices;
296 VkQueueFamilyProperties *queueFamiliesProperties = NULL;
297 uint32_t queueFamiliesPropertiesAllocatedSize = 0;
298 VkExtensionProperties *deviceExtensions = NULL;
299 uint32_t deviceExtensionsAllocatedSize = 0;
300 uint32_t physicalDeviceIndex;
301 VkResult result;
302
303 result = context->vkEnumeratePhysicalDevices(context->instance, &physicalDeviceCount, NULL);
304 if (result != VK_SUCCESS) {
305 return SDL_SetError("vkEnumeratePhysicalDevices(): %s", getVulkanResultString(result));
306 }
307 if (physicalDeviceCount == 0) {
308 return SDL_SetError("vkEnumeratePhysicalDevices(): no physical devices");
309 }
310 physicalDevices = (VkPhysicalDevice *)SDL_malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount);
311 if (!physicalDevices) {
312 return -1;
313 }
314 result = context->vkEnumeratePhysicalDevices(context->instance, &physicalDeviceCount, physicalDevices);
315 if (result != VK_SUCCESS) {
316 SDL_free(physicalDevices);
317 return SDL_SetError("vkEnumeratePhysicalDevices(): %s", getVulkanResultString(result));
318 }
319 context->physicalDevice = NULL;
320 for (physicalDeviceIndex = 0; physicalDeviceIndex < physicalDeviceCount; physicalDeviceIndex++) {
321 uint32_t queueFamiliesCount = 0;
322 uint32_t queueFamilyIndex;
323 uint32_t deviceExtensionCount = 0;
324 bool hasSwapchainExtension = false;
325 uint32_t i;
326
327 VkPhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex];
328 context->vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, NULL);
329 if (queueFamiliesCount == 0) {
330 continue;
331 }
332 if (queueFamiliesPropertiesAllocatedSize < queueFamiliesCount) {
333 SDL_free(queueFamiliesProperties);
334 queueFamiliesPropertiesAllocatedSize = queueFamiliesCount;
335 queueFamiliesProperties = (VkQueueFamilyProperties *)SDL_malloc(sizeof(VkQueueFamilyProperties) * queueFamiliesPropertiesAllocatedSize);
336 if (!queueFamiliesProperties) {
337 SDL_free(physicalDevices);
338 SDL_free(deviceExtensions);
339 return -1;
340 }
341 }
342 context->vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, queueFamiliesProperties);
343
344 // Initialize timestampValidBits for scoring in selectQueueFamily
345 for (queueFamilyIndex = 0; queueFamilyIndex < queueFamiliesCount; queueFamilyIndex++) {
346 queueFamiliesProperties[queueFamilyIndex].timestampValidBits = 0;
347 }
348 context->presentQueueFamilyIndex = -1;
349 context->graphicsQueueFamilyIndex = -1;
350 for (queueFamilyIndex = 0; queueFamilyIndex < queueFamiliesCount; queueFamilyIndex++) {
351 VkBool32 supported = 0;
352
353 if (queueFamiliesProperties[queueFamilyIndex].queueCount == 0) {
354 continue;
355 }
356
357 if (queueFamiliesProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
358 context->graphicsQueueFamilyIndex = queueFamilyIndex;
359 }
360
361 result = context->vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, context->surface, &supported);
362 if (result == VK_SUCCESS) {
363 if (supported) {
364 context->presentQueueFamilyIndex = queueFamilyIndex;
365 if (queueFamiliesProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
366 break; // use this queue because it can present and do graphics
367 }
368 }
369 }
370 }
371 if (context->presentQueueFamilyIndex < 0 || context->graphicsQueueFamilyIndex < 0) {
372 // We can't render and present on this device
373 continue;
374 }
375
376 context->presentQueueCount = queueFamiliesProperties[context->presentQueueFamilyIndex].queueCount;
377 ++queueFamiliesProperties[context->presentQueueFamilyIndex].timestampValidBits;
378 context->graphicsQueueCount = queueFamiliesProperties[context->graphicsQueueFamilyIndex].queueCount;
379 ++queueFamiliesProperties[context->graphicsQueueFamilyIndex].timestampValidBits;
380
381 context->transferQueueFamilyIndex = selectQueueFamily(queueFamiliesProperties, queueFamiliesCount, VK_QUEUE_TRANSFER_BIT, &context->transferQueueCount);
382 context->computeQueueFamilyIndex = selectQueueFamily(queueFamiliesProperties, queueFamiliesCount, VK_QUEUE_COMPUTE_BIT, &context->computeQueueCount);
383 context->decodeQueueFamilyIndex = selectQueueFamily(queueFamiliesProperties, queueFamiliesCount, VK_QUEUE_VIDEO_DECODE_BIT_KHR, &context->decodeQueueCount);
384 if (context->transferQueueFamilyIndex < 0) {
385 // ffmpeg can fall back to the compute or graphics queues for this
386 context->transferQueueFamilyIndex = selectQueueFamily(queueFamiliesProperties, queueFamiliesCount, VK_QUEUE_COMPUTE_BIT, &context->transferQueueCount);
387 if (context->transferQueueFamilyIndex < 0) {
388 context->transferQueueFamilyIndex = selectQueueFamily(queueFamiliesProperties, queueFamiliesCount, VK_QUEUE_GRAPHICS_BIT, &context->transferQueueCount);
389 }
390 }
391
392 if (context->transferQueueFamilyIndex < 0 ||
393 context->computeQueueFamilyIndex < 0) {
394 // This device doesn't have the queues we need for video decoding
395 continue;
396 }
397
398 result = context->vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, NULL);
399 if (result != VK_SUCCESS) {
400 SDL_free(physicalDevices);
401 SDL_free(queueFamiliesProperties);
402 SDL_free(deviceExtensions);
403 return SDL_SetError("vkEnumerateDeviceExtensionProperties(): %s", getVulkanResultString(result));
404 }
405 if (deviceExtensionCount == 0) {
406 continue;
407 }
408 if (deviceExtensionsAllocatedSize < deviceExtensionCount) {
409 SDL_free(deviceExtensions);
410 deviceExtensionsAllocatedSize = deviceExtensionCount;
411 deviceExtensions = SDL_malloc(sizeof(VkExtensionProperties) * deviceExtensionsAllocatedSize);
412 if (!deviceExtensions) {
413 SDL_free(physicalDevices);
414 SDL_free(queueFamiliesProperties);
415 return -1;
416 }
417 }
418 result = context->vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, deviceExtensions);
419 if (result != VK_SUCCESS) {
420 SDL_free(physicalDevices);
421 SDL_free(queueFamiliesProperties);
422 SDL_free(deviceExtensions);
423 return SDL_SetError("vkEnumerateDeviceExtensionProperties(): %s", getVulkanResultString(result));
424 }
425 for (i = 0; i < deviceExtensionCount; i++) {
426 if (SDL_strcmp(deviceExtensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0) {
427 hasSwapchainExtension = true;
428 break;
429 }
430 }
431 if (!hasSwapchainExtension) {
432 continue;
433 }
434 context->physicalDevice = physicalDevice;
435 break;
436 }
437 SDL_free(physicalDevices);
438 SDL_free(queueFamiliesProperties);
439 SDL_free(deviceExtensions);
440 if (!context->physicalDevice) {
441 return SDL_SetError("Vulkan: no viable physical devices found");
442 }
443 return 0;
444}
445
446static void initDeviceFeatures(VulkanDeviceFeatures *features)
447{
448 features->device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
449 features->device_features.pNext = &features->device_features_1_1;
450 features->device_features_1_1.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
451 features->device_features_1_1.pNext = &features->device_features_1_2;
452 features->device_features_1_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
453 features->device_features_1_2.pNext = &features->device_features_1_3;
454 features->device_features_1_3.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
455 features->device_features_1_3.pNext = &features->desc_buf_features;
456 features->desc_buf_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT;
457 features->desc_buf_features.pNext = &features->atomic_float_features;
458 features->atomic_float_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_FEATURES_EXT;
459 features->atomic_float_features.pNext = &features->coop_matrix_features;
460 features->coop_matrix_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_KHR;
461 features->coop_matrix_features.pNext = NULL;
462}
463
464static void copyDeviceFeatures(VulkanDeviceFeatures *supported_features, VulkanDeviceFeatures *requested_features)
465{
466#define COPY_OPTIONAL_FEATURE(X) requested_features->X = supported_features->X
467 COPY_OPTIONAL_FEATURE(device_features.features.shaderImageGatherExtended);
468 COPY_OPTIONAL_FEATURE(device_features.features.shaderStorageImageReadWithoutFormat);
469 COPY_OPTIONAL_FEATURE(device_features.features.shaderStorageImageWriteWithoutFormat);
470 COPY_OPTIONAL_FEATURE(device_features.features.fragmentStoresAndAtomics);
471 COPY_OPTIONAL_FEATURE(device_features.features.vertexPipelineStoresAndAtomics);
472 COPY_OPTIONAL_FEATURE(device_features.features.shaderInt64);
473 COPY_OPTIONAL_FEATURE(device_features.features.shaderInt16);
474 COPY_OPTIONAL_FEATURE(device_features.features.shaderFloat64);
475 COPY_OPTIONAL_FEATURE(device_features_1_1.samplerYcbcrConversion);
476 COPY_OPTIONAL_FEATURE(device_features_1_1.storagePushConstant16);
477 COPY_OPTIONAL_FEATURE(device_features_1_2.bufferDeviceAddress);
478 COPY_OPTIONAL_FEATURE(device_features_1_2.hostQueryReset);
479 COPY_OPTIONAL_FEATURE(device_features_1_2.storagePushConstant8);
480 COPY_OPTIONAL_FEATURE(device_features_1_2.shaderInt8);
481 COPY_OPTIONAL_FEATURE(device_features_1_2.storageBuffer8BitAccess);
482 COPY_OPTIONAL_FEATURE(device_features_1_2.uniformAndStorageBuffer8BitAccess);
483 COPY_OPTIONAL_FEATURE(device_features_1_2.shaderFloat16);
484 COPY_OPTIONAL_FEATURE(device_features_1_2.shaderSharedInt64Atomics);
485 COPY_OPTIONAL_FEATURE(device_features_1_2.vulkanMemoryModel);
486 COPY_OPTIONAL_FEATURE(device_features_1_2.vulkanMemoryModelDeviceScope);
487 COPY_OPTIONAL_FEATURE(device_features_1_2.hostQueryReset);
488 COPY_OPTIONAL_FEATURE(device_features_1_3.dynamicRendering);
489 COPY_OPTIONAL_FEATURE(device_features_1_3.maintenance4);
490 COPY_OPTIONAL_FEATURE(device_features_1_3.synchronization2);
491 COPY_OPTIONAL_FEATURE(device_features_1_3.computeFullSubgroups);
492 COPY_OPTIONAL_FEATURE(device_features_1_3.shaderZeroInitializeWorkgroupMemory);
493 COPY_OPTIONAL_FEATURE(desc_buf_features.descriptorBuffer);
494 COPY_OPTIONAL_FEATURE(desc_buf_features.descriptorBufferPushDescriptors);
495 COPY_OPTIONAL_FEATURE(atomic_float_features.shaderBufferFloat32Atomics);
496 COPY_OPTIONAL_FEATURE(atomic_float_features.shaderBufferFloat32AtomicAdd);
497 COPY_OPTIONAL_FEATURE(coop_matrix_features.cooperativeMatrix);
498#undef COPY_OPTIONAL_FEATURE
499
500 // timeline semaphores is required by ffmpeg
501 requested_features->device_features_1_2.timelineSemaphore = 1;
502}
503
504static int addQueueFamily(VkDeviceQueueCreateInfo **pQueueCreateInfos, uint32_t *pQueueCreateInfoCount, uint32_t queueFamilyIndex, uint32_t queueCount)
505{
506 VkDeviceQueueCreateInfo *queueCreateInfo;
507 VkDeviceQueueCreateInfo *queueCreateInfos = *pQueueCreateInfos;
508 uint32_t queueCreateInfoCount = *pQueueCreateInfoCount;
509 float *queuePriorities;
510
511 if (queueCount == 0) {
512 return 0;
513 }
514
515 for (uint32_t i = 0; i < queueCreateInfoCount; ++i) {
516 if (queueCreateInfos[i].queueFamilyIndex == queueFamilyIndex) {
517 return 0;
518 }
519 }
520
521 queueCreateInfos = (VkDeviceQueueCreateInfo *)SDL_realloc(queueCreateInfos, (queueCreateInfoCount + 1) * sizeof(*queueCreateInfos));
522 if (!queueCreateInfos) {
523 return -1;
524 }
525
526 queuePriorities = (float *)SDL_malloc(queueCount * sizeof(*queuePriorities));
527 if (!queuePriorities) {
528 return -1;
529 }
530
531 for (uint32_t i = 0; i < queueCount; ++i) {
532 queuePriorities[i] = 1.0f / queueCount;
533 }
534
535 queueCreateInfo = &queueCreateInfos[queueCreateInfoCount++];
536 SDL_zerop(queueCreateInfo);
537 queueCreateInfo->sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
538 queueCreateInfo->queueFamilyIndex = queueFamilyIndex;
539 queueCreateInfo->queueCount = queueCount;
540 queueCreateInfo->pQueuePriorities = queuePriorities;
541
542 *pQueueCreateInfos = queueCreateInfos;
543 *pQueueCreateInfoCount = queueCreateInfoCount;
544 return 0;
545}
546
547static int createDevice(VulkanVideoContext *context)
548{
549 static const char *const deviceExtensionNames[] = {
550 VK_KHR_SWAPCHAIN_EXTENSION_NAME,
551 VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
552 VK_KHR_MAINTENANCE1_EXTENSION_NAME,
553 VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
554 VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,
555 };
556 static const char *optional_extensions[] = {
557 VK_KHR_VIDEO_QUEUE_EXTENSION_NAME,
558 VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME,
559 VK_KHR_VIDEO_DECODE_H264_EXTENSION_NAME,
560 VK_KHR_VIDEO_DECODE_H265_EXTENSION_NAME,
561 VK_KHR_VIDEO_DECODE_AV1_EXTENSION_NAME
562 };
563 VkDeviceCreateInfo deviceCreateInfo = { 0 };
564 VkDeviceQueueCreateInfo *queueCreateInfos = NULL;
565 uint32_t queueCreateInfoCount = 0;
566 VulkanDeviceFeatures supported_features;
567 VkResult result = VK_ERROR_UNKNOWN;
568
569 if (addQueueFamily(&queueCreateInfos, &queueCreateInfoCount, context->presentQueueFamilyIndex, context->presentQueueCount) < 0 ||
570 addQueueFamily(&queueCreateInfos, &queueCreateInfoCount, context->graphicsQueueFamilyIndex, context->graphicsQueueCount) < 0 ||
571 addQueueFamily(&queueCreateInfos, &queueCreateInfoCount, context->transferQueueFamilyIndex, context->transferQueueCount) < 0 ||
572 addQueueFamily(&queueCreateInfos, &queueCreateInfoCount, context->computeQueueFamilyIndex, context->computeQueueCount) < 0 ||
573 addQueueFamily(&queueCreateInfos, &queueCreateInfoCount, context->decodeQueueFamilyIndex, context->decodeQueueCount) < 0) {
574 goto done;
575 }
576
577 initDeviceFeatures(&supported_features);
578 initDeviceFeatures(&context->features);
579 context->vkGetPhysicalDeviceFeatures2(context->physicalDevice, &supported_features.device_features);
580 copyDeviceFeatures(&supported_features, &context->features);
581
582 deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
583 deviceCreateInfo.queueCreateInfoCount = queueCreateInfoCount;
584 deviceCreateInfo.pQueueCreateInfos = queueCreateInfos;
585 deviceCreateInfo.pEnabledFeatures = NULL;
586 deviceCreateInfo.enabledExtensionCount = SDL_arraysize(deviceExtensionNames);
587 deviceCreateInfo.pNext = &context->features.device_features;
588
589 const char **deviceExtensionsCopy = SDL_calloc(deviceCreateInfo.enabledExtensionCount + SDL_arraysize(optional_extensions), sizeof(const char *));
590 for (uint32_t i = 0; i < deviceCreateInfo.enabledExtensionCount; i++) {
591 deviceExtensionsCopy[i] = deviceExtensionNames[i];
592 }
593
594 // Get the rest of the optional extensions
595 {
596 uint32_t extensionCount;
597 if (context->vkEnumerateDeviceExtensionProperties(context->physicalDevice, NULL, &extensionCount, NULL) == VK_SUCCESS && extensionCount > 0) {
598 VkExtensionProperties *extensionProperties = SDL_calloc(extensionCount, sizeof(VkExtensionProperties));
599 if (context->vkEnumerateDeviceExtensionProperties(context->physicalDevice, NULL, &extensionCount, extensionProperties) == VK_SUCCESS) {
600 for (uint32_t i = 0; i < SDL_arraysize(optional_extensions); ++i) {
601 for (uint32_t j = 0; j < extensionCount; ++j) {
602 if (SDL_strcmp(extensionProperties[j].extensionName, optional_extensions[i]) == 0) {
603 deviceExtensionsCopy[deviceCreateInfo.enabledExtensionCount++] = optional_extensions[i];
604 break;
605 }
606 }
607 }
608 }
609 SDL_free(extensionProperties);
610 }
611 }
612 deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionsCopy;
613
614 context->deviceExtensions = deviceExtensionsCopy;
615 context->deviceExtensionsCount = deviceCreateInfo.enabledExtensionCount;
616
617 result = context->vkCreateDevice(context->physicalDevice, &deviceCreateInfo, NULL, &context->device);
618 if (result != VK_SUCCESS) {
619 SDL_SetError("vkCreateDevice(): %s", getVulkanResultString(result));
620 goto done;
621 }
622
623 if (loadDeviceFunctions(context) < 0) {
624 result = VK_ERROR_UNKNOWN;
625 context->device = VK_NULL_HANDLE;
626 goto done;
627 }
628
629 // Get the graphics queue that SDL will use
630 context->vkGetDeviceQueue(context->device, context->graphicsQueueFamilyIndex, 0, &context->graphicsQueue);
631
632 // Create a command pool
633 VkCommandPoolCreateInfo commandPoolCreateInfo = { 0 };
634 commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
635 commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
636 commandPoolCreateInfo.queueFamilyIndex = context->graphicsQueueFamilyIndex;
637 result = context->vkCreateCommandPool(context->device, &commandPoolCreateInfo, NULL, &context->commandPool);
638 if (result != VK_SUCCESS) {
639 SDL_SetError("vkCreateCommandPool(): %s", getVulkanResultString(result));
640 goto done;
641 }
642
643done:
644 for (uint32_t i = 0; i < queueCreateInfoCount; ++i) {
645 SDL_free((void *)queueCreateInfos[i].pQueuePriorities);
646 }
647 SDL_free(queueCreateInfos);
648
649 if (result != VK_SUCCESS) {
650 return -1;
651 }
652 return 0;
653}
654
655VulkanVideoContext *CreateVulkanVideoContext(SDL_Window *window)
656{
657 VulkanVideoContext *context = SDL_calloc(1, sizeof(*context));
658 if (!context) {
659 return NULL;
660 }
661 if (loadGlobalFunctions(context) < 0 ||
662 createInstance(context) < 0 ||
663 createSurface(context, window) < 0 ||
664 findPhysicalDevice(context) < 0 ||
665 createDevice(context) < 0) {
666 DestroyVulkanVideoContext(context);
667 return NULL;
668 }
669 return context;
670}
671
672void SetupVulkanRenderProperties(VulkanVideoContext *context, SDL_PropertiesID props)
673{
674 SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER, context->instance);
675 SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_SURFACE_NUMBER, (Sint64)context->surface);
676 SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_PHYSICAL_DEVICE_POINTER, context->physicalDevice);
677 SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER, context->device);
678 SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER, context->presentQueueFamilyIndex);
679 SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER, context->graphicsQueueFamilyIndex);
680}
681
682#if LIBAVUTIL_VERSION_MAJOR >= 59
683static void AddQueueFamily(AVVulkanDeviceContext *ctx, int idx, int num, VkQueueFlagBits flags)
684{
685 AVVulkanDeviceQueueFamily *entry = &ctx->qf[ctx->nb_qf++];
686 entry->idx = idx;
687 entry->num = num;
688 entry->flags = flags;
689}
690#endif /* LIBAVUTIL_VERSION_MAJOR */
691
692void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceContext *ctx)
693{
694 ctx->get_proc_addr = context->vkGetInstanceProcAddr;
695 ctx->inst = context->instance;
696 ctx->phys_dev = context->physicalDevice;
697 ctx->act_dev = context->device;
698 ctx->device_features = context->features.device_features;
699 ctx->enabled_inst_extensions = context->instanceExtensions;
700 ctx->nb_enabled_inst_extensions = context->instanceExtensionsCount;
701 ctx->enabled_dev_extensions = context->deviceExtensions;
702 ctx->nb_enabled_dev_extensions = context->deviceExtensionsCount;
703#if LIBAVUTIL_VERSION_MAJOR >= 59
704 AddQueueFamily(ctx, context->graphicsQueueFamilyIndex, context->graphicsQueueCount, VK_QUEUE_GRAPHICS_BIT);
705 AddQueueFamily(ctx, context->transferQueueFamilyIndex, context->transferQueueCount, VK_QUEUE_TRANSFER_BIT);
706 AddQueueFamily(ctx, context->computeQueueFamilyIndex, context->computeQueueCount, VK_QUEUE_COMPUTE_BIT);
707 AddQueueFamily(ctx, context->decodeQueueFamilyIndex, context->decodeQueueCount, VK_QUEUE_VIDEO_DECODE_BIT_KHR);
708#else
709 ctx->queue_family_index = context->graphicsQueueFamilyIndex;
710 ctx->nb_graphics_queues = context->graphicsQueueCount;
711 ctx->queue_family_tx_index = context->transferQueueFamilyIndex;
712 ctx->nb_tx_queues = context->transferQueueCount;
713 ctx->queue_family_comp_index = context->computeQueueFamilyIndex;
714 ctx->nb_comp_queues = context->computeQueueCount;
715 ctx->queue_family_encode_index = -1;
716 ctx->nb_encode_queues = 0;
717 ctx->queue_family_decode_index = context->decodeQueueFamilyIndex;
718 ctx->nb_decode_queues = context->decodeQueueCount;
719#endif /* LIBAVUTIL_VERSION_MAJOR */
720}
721
722static int CreateCommandBuffers(VulkanVideoContext *context, SDL_Renderer *renderer)
723{
724 uint32_t commandBufferCount = (uint32_t)SDL_GetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_VULKAN_SWAPCHAIN_IMAGE_COUNT_NUMBER, 1);
725
726 if (commandBufferCount > context->waitSemaphoreCount) {
727 VkSemaphore *semaphores = (VkSemaphore *)SDL_realloc(context->waitSemaphores, commandBufferCount * sizeof(*semaphores));
728 if (!semaphores) {
729 return -1;
730 }
731 context->waitSemaphores = semaphores;
732
733 VkSemaphoreCreateInfo semaphoreCreateInfo = { 0 };
734 semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
735 while (context->waitSemaphoreCount < commandBufferCount) {
736 VkResult result = context->vkCreateSemaphore(context->device, &semaphoreCreateInfo, NULL, &context->waitSemaphores[context->waitSemaphoreCount]);
737 if (result != VK_SUCCESS) {
738 SDL_SetError("vkCreateSemaphore(): %s", getVulkanResultString(result));
739 return -1;
740 }
741 ++context->waitSemaphoreCount;
742 }
743 }
744
745 if (commandBufferCount > context->signalSemaphoreCount) {
746 VkSemaphore *semaphores = (VkSemaphore *)SDL_realloc(context->signalSemaphores, commandBufferCount * sizeof(*semaphores));
747 if (!semaphores) {
748 return -1;
749 }
750 context->signalSemaphores = semaphores;
751
752 VkSemaphoreCreateInfo semaphoreCreateInfo = { 0 };
753 semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
754 while (context->signalSemaphoreCount < commandBufferCount) {
755 VkResult result = context->vkCreateSemaphore(context->device, &semaphoreCreateInfo, NULL, &context->signalSemaphores[context->signalSemaphoreCount]);
756 if (result != VK_SUCCESS) {
757 SDL_SetError("vkCreateSemaphore(): %s", getVulkanResultString(result));
758 return -1;
759 }
760 ++context->signalSemaphoreCount;
761 }
762 }
763
764 if (commandBufferCount > context->commandBufferCount) {
765 uint32_t needed = (commandBufferCount - context->commandBufferCount);
766 VkCommandBuffer *commandBuffers = (VkCommandBuffer *)SDL_realloc(context->commandBuffers, commandBufferCount * sizeof(*commandBuffers));
767 if (!commandBuffers) {
768 return -1;
769 }
770 context->commandBuffers = commandBuffers;
771
772 VkCommandBufferAllocateInfo commandBufferAllocateInfo = { 0 };
773 commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
774 commandBufferAllocateInfo.commandPool = context->commandPool;
775 commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
776 commandBufferAllocateInfo.commandBufferCount = needed;
777 VkResult result = context->vkAllocateCommandBuffers(context->device, &commandBufferAllocateInfo, &context->commandBuffers[context->commandBufferCount]);
778 if (result != VK_SUCCESS) {
779 SDL_SetError("vkAllocateCommandBuffers(): %s", getVulkanResultString(result));
780 return -1;
781 }
782
783 context->commandBufferCount = commandBufferCount;
784 }
785 return 0;
786}
787
788int BeginVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer)
789{
790 AVHWFramesContext *frames = (AVHWFramesContext *)(frame->hw_frames_ctx->data);
791 AVVulkanFramesContext *vk = (AVVulkanFramesContext *)(frames->hwctx);
792 AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0];
793
794 if (CreateCommandBuffers(context, renderer) < 0) {
795 return -1;
796 }
797
798 vk->lock_frame(frames, pVkFrame);
799
800 VkTimelineSemaphoreSubmitInfo timeline = { 0 };
801 timeline.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO;
802 timeline.waitSemaphoreValueCount = 1;
803 timeline.pWaitSemaphoreValues = pVkFrame->sem_value;
804
805 VkPipelineStageFlags pipelineStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
806 VkSubmitInfo submitInfo = { 0 };
807 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
808 submitInfo.waitSemaphoreCount = 1;
809 submitInfo.pWaitSemaphores = pVkFrame->sem;
810 submitInfo.pWaitDstStageMask = &pipelineStageMask;
811 submitInfo.signalSemaphoreCount = 1;
812 submitInfo.pSignalSemaphores = &context->waitSemaphores[context->commandBufferIndex];
813 submitInfo.pNext = &timeline;
814
815 if (pVkFrame->layout[0] != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
816 VkCommandBuffer commandBuffer = context->commandBuffers[context->commandBufferIndex];
817
818 VkCommandBufferBeginInfo beginInfo = { 0 };
819 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
820 beginInfo.flags = 0;
821 context->vkBeginCommandBuffer(commandBuffer, &beginInfo);
822
823 VkImageMemoryBarrier2 barrier = { 0 };
824 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
825 barrier.srcAccessMask = VK_ACCESS_2_NONE;
826 barrier.dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT;
827 barrier.oldLayout = pVkFrame->layout[0];
828 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
829 barrier.image = pVkFrame->img[0];
830 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
831 barrier.subresourceRange.levelCount = 1;
832 barrier.subresourceRange.layerCount = 1;
833 barrier.srcQueueFamilyIndex = pVkFrame->queue_family[0];
834 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
835 barrier.srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
836 barrier.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
837
838 VkDependencyInfo dep = { 0 };
839 dep.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
840 dep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
841 dep.imageMemoryBarrierCount = 1;
842 dep.pImageMemoryBarriers = &barrier;
843 context->vkCmdPipelineBarrier2(commandBuffer, &dep);
844
845 context->vkEndCommandBuffer(commandBuffer);
846
847 // Add the image barrier to the submit info
848 submitInfo.commandBufferCount = 1;
849 submitInfo.pCommandBuffers = &context->commandBuffers[context->commandBufferIndex];
850
851 pVkFrame->layout[0] = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
852 pVkFrame->queue_family[0] = VK_QUEUE_FAMILY_IGNORED;
853 }
854
855 VkResult result = context->vkQueueSubmit(context->graphicsQueue, 1, &submitInfo, 0);
856 if (result != VK_SUCCESS) {
857 // Don't return an error here, we need to complete the frame operation
858 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION , "vkQueueSubmit(): %s", getVulkanResultString(result));
859 }
860
861 SDL_AddVulkanRenderSemaphores(renderer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, (Sint64)context->waitSemaphores[context->commandBufferIndex], (Sint64)context->signalSemaphores[context->commandBufferIndex]);
862
863 return 0;
864}
865
866int FinishVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer)
867{
868 AVHWFramesContext *frames = (AVHWFramesContext *)(frame->hw_frames_ctx->data);
869 AVVulkanFramesContext *vk = (AVVulkanFramesContext *)(frames->hwctx);
870 AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0];
871
872 // Transition the frame back to ffmpeg
873 ++pVkFrame->sem_value[0];
874
875 VkTimelineSemaphoreSubmitInfo timeline = { 0 };
876 timeline.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO;
877 timeline.signalSemaphoreValueCount = 1;
878 timeline.pSignalSemaphoreValues = pVkFrame->sem_value;
879
880 VkPipelineStageFlags pipelineStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
881 VkSubmitInfo submitInfo = { 0 };
882 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
883 submitInfo.waitSemaphoreCount = 1;
884 submitInfo.pWaitSemaphores = &context->signalSemaphores[context->commandBufferIndex];
885 submitInfo.pWaitDstStageMask = &pipelineStageMask;
886 submitInfo.signalSemaphoreCount = 1;
887 submitInfo.pSignalSemaphores = pVkFrame->sem;
888 submitInfo.pNext = &timeline;
889
890 VkResult result = context->vkQueueSubmit(context->graphicsQueue, 1, &submitInfo, 0);
891 if (result != VK_SUCCESS) {
892 // Don't return an error here, we need to complete the frame operation
893 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "vkQueueSubmit(): %s", getVulkanResultString(result));
894 }
895
896 vk->unlock_frame(frames, pVkFrame);
897
898 context->commandBufferIndex = (context->commandBufferIndex + 1) % context->commandBufferCount;
899
900 return 0;
901}
902
903SDL_Texture *CreateVulkanVideoTexture(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer, SDL_PropertiesID props)
904{
905 AVHWFramesContext *frames = (AVHWFramesContext *)(frame->hw_frames_ctx->data);
906 AVVulkanFramesContext *vk = (AVVulkanFramesContext *)(frames->hwctx);
907 AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0];
908 Uint32 format;
909
910 switch (vk->format[0]) {
911 case VK_FORMAT_G8B8G8R8_422_UNORM:
912 format = SDL_PIXELFORMAT_YUY2;
913 break;
914 case VK_FORMAT_B8G8R8G8_422_UNORM:
915 format = SDL_PIXELFORMAT_UYVY;
916 break;
917 case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
918 format = SDL_PIXELFORMAT_IYUV;
919 break;
920 case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
921 format = SDL_PIXELFORMAT_NV12;
922 break;
923 case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
924 format = SDL_PIXELFORMAT_P010;
925 break;
926 default:
927 format = SDL_PIXELFORMAT_UNKNOWN;
928 break;
929 }
930 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format);
931 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER, (Sint64)pVkFrame->img[0]);
932 return SDL_CreateTextureWithProperties(renderer, props);
933}
934
935void DestroyVulkanVideoContext(VulkanVideoContext *context)
936{
937 if (context) {
938 if (context->device) {
939 context->vkDeviceWaitIdle(context->device);
940 }
941 if (context->instanceExtensions) {
942 SDL_free(context->instanceExtensions);
943 }
944 if (context->deviceExtensions) {
945 SDL_free(context->deviceExtensions);
946 }
947 if (context->waitSemaphores) {
948 for (uint32_t i = 0; i < context->waitSemaphoreCount; ++i) {
949 context->vkDestroySemaphore(context->device, context->waitSemaphores[i], NULL);
950 }
951 SDL_free(context->waitSemaphores);
952 context->waitSemaphores = NULL;
953 }
954 if (context->signalSemaphores) {
955 for (uint32_t i = 0; i < context->signalSemaphoreCount; ++i) {
956 context->vkDestroySemaphore(context->device, context->signalSemaphores[i], NULL);
957 }
958 SDL_free(context->signalSemaphores);
959 context->signalSemaphores = NULL;
960 }
961 if (context->commandBuffers) {
962 context->vkFreeCommandBuffers(context->device, context->commandPool, context->commandBufferCount, context->commandBuffers);
963 SDL_free(context->commandBuffers);
964 context->commandBuffers = NULL;
965 }
966 if (context->commandPool) {
967 context->vkDestroyCommandPool(context->device, context->commandPool, NULL);
968 context->commandPool = VK_NULL_HANDLE;
969 }
970 if (context->device) {
971 context->vkDestroyDevice(context->device, NULL);
972 }
973 if (context->surface) {
974 context->vkDestroySurfaceKHR(context->instance, context->surface, NULL);
975 }
976 if (context->instance) {
977 context->vkDestroyInstance(context->instance, NULL);
978 }
979 SDL_free(context);
980 }
981}
982
983#else
984
985VulkanVideoContext *CreateVulkanVideoContext(SDL_Window *window)
986{
987 SDL_SetError("testffmpeg not built with Vulkan support");
988 return NULL;
989}
990
991void SetupVulkanRenderProperties(VulkanVideoContext *context, SDL_PropertiesID props)
992{
993}
994
995void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceContext *ctx)
996{
997}
998
999SDL_Texture *CreateVulkanVideoTexture(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer, SDL_PropertiesID props)
1000{
1001 return NULL;
1002}
1003
1004int BeginVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer)
1005{
1006 return -1;
1007}
1008
1009int FinishVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer)
1010{
1011 return -1;
1012}
1013
1014void DestroyVulkanVideoContext(VulkanVideoContext *context)
1015{
1016}
1017
1018#endif // FFMPEG_VULKAN_SUPPORT