Simple Directmedia Layer
at main 45 kB view raw
1/* 2 Copyright (C) 1997-2024 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\n"); \ 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\n", 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 682void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceContext *ctx) 683{ 684 ctx->get_proc_addr = context->vkGetInstanceProcAddr; 685 ctx->inst = context->instance; 686 ctx->phys_dev = context->physicalDevice; 687 ctx->act_dev = context->device; 688 ctx->device_features = context->features.device_features; 689 ctx->enabled_inst_extensions = context->instanceExtensions; 690 ctx->nb_enabled_inst_extensions = context->instanceExtensionsCount; 691 ctx->enabled_dev_extensions = context->deviceExtensions; 692 ctx->nb_enabled_dev_extensions = context->deviceExtensionsCount; 693 ctx->queue_family_index = context->graphicsQueueFamilyIndex; 694 ctx->nb_graphics_queues = context->graphicsQueueCount; 695 ctx->queue_family_tx_index = context->transferQueueFamilyIndex; 696 ctx->nb_tx_queues = context->transferQueueCount; 697 ctx->queue_family_comp_index = context->computeQueueFamilyIndex; 698 ctx->nb_comp_queues = context->computeQueueCount; 699 ctx->queue_family_encode_index = -1; 700 ctx->nb_encode_queues = 0; 701 ctx->queue_family_decode_index = context->decodeQueueFamilyIndex; 702 ctx->nb_decode_queues = context->decodeQueueCount; 703} 704 705static int CreateCommandBuffers(VulkanVideoContext *context, SDL_Renderer *renderer) 706{ 707 uint32_t commandBufferCount = (uint32_t)SDL_GetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_VULKAN_SWAPCHAIN_IMAGE_COUNT_NUMBER, 1); 708 709 if (commandBufferCount > context->waitSemaphoreCount) { 710 VkSemaphore *semaphores = (VkSemaphore *)SDL_realloc(context->waitSemaphores, commandBufferCount * sizeof(*semaphores)); 711 if (!semaphores) { 712 return -1; 713 } 714 context->waitSemaphores = semaphores; 715 716 VkSemaphoreCreateInfo semaphoreCreateInfo = { 0 }; 717 semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; 718 while (context->waitSemaphoreCount < commandBufferCount) { 719 VkResult result = context->vkCreateSemaphore(context->device, &semaphoreCreateInfo, NULL, &context->waitSemaphores[context->waitSemaphoreCount]); 720 if (result != VK_SUCCESS) { 721 SDL_SetError("vkCreateSemaphore(): %s", getVulkanResultString(result)); 722 return -1; 723 } 724 ++context->waitSemaphoreCount; 725 } 726 } 727 728 if (commandBufferCount > context->signalSemaphoreCount) { 729 VkSemaphore *semaphores = (VkSemaphore *)SDL_realloc(context->signalSemaphores, commandBufferCount * sizeof(*semaphores)); 730 if (!semaphores) { 731 return -1; 732 } 733 context->signalSemaphores = semaphores; 734 735 VkSemaphoreCreateInfo semaphoreCreateInfo = { 0 }; 736 semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; 737 while (context->signalSemaphoreCount < commandBufferCount) { 738 VkResult result = context->vkCreateSemaphore(context->device, &semaphoreCreateInfo, NULL, &context->signalSemaphores[context->signalSemaphoreCount]); 739 if (result != VK_SUCCESS) { 740 SDL_SetError("vkCreateSemaphore(): %s", getVulkanResultString(result)); 741 return -1; 742 } 743 ++context->signalSemaphoreCount; 744 } 745 } 746 747 if (commandBufferCount > context->commandBufferCount) { 748 uint32_t needed = (commandBufferCount - context->commandBufferCount); 749 VkCommandBuffer *commandBuffers = (VkCommandBuffer *)SDL_realloc(context->commandBuffers, commandBufferCount * sizeof(*commandBuffers)); 750 if (!commandBuffers) { 751 return -1; 752 } 753 context->commandBuffers = commandBuffers; 754 755 VkCommandBufferAllocateInfo commandBufferAllocateInfo = { 0 }; 756 commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; 757 commandBufferAllocateInfo.commandPool = context->commandPool; 758 commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; 759 commandBufferAllocateInfo.commandBufferCount = needed; 760 VkResult result = context->vkAllocateCommandBuffers(context->device, &commandBufferAllocateInfo, &context->commandBuffers[context->commandBufferCount]); 761 if (result != VK_SUCCESS) { 762 SDL_SetError("vkAllocateCommandBuffers(): %s", getVulkanResultString(result)); 763 return -1; 764 } 765 766 context->commandBufferCount = commandBufferCount; 767 } 768 return 0; 769} 770 771int BeginVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer) 772{ 773 AVHWFramesContext *frames = (AVHWFramesContext *)(frame->hw_frames_ctx->data); 774 AVVulkanFramesContext *vk = (AVVulkanFramesContext *)(frames->hwctx); 775 AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0]; 776 777 if (CreateCommandBuffers(context, renderer) < 0) { 778 return -1; 779 } 780 781 vk->lock_frame(frames, pVkFrame); 782 783 VkTimelineSemaphoreSubmitInfo timeline = { 0 }; 784 timeline.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO; 785 timeline.waitSemaphoreValueCount = 1; 786 timeline.pWaitSemaphoreValues = pVkFrame->sem_value; 787 788 VkPipelineStageFlags pipelineStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; 789 VkSubmitInfo submitInfo = { 0 }; 790 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; 791 submitInfo.waitSemaphoreCount = 1; 792 submitInfo.pWaitSemaphores = pVkFrame->sem; 793 submitInfo.pWaitDstStageMask = &pipelineStageMask; 794 submitInfo.signalSemaphoreCount = 1; 795 submitInfo.pSignalSemaphores = &context->waitSemaphores[context->commandBufferIndex]; 796 submitInfo.pNext = &timeline; 797 798 if (pVkFrame->layout[0] != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { 799 VkCommandBuffer commandBuffer = context->commandBuffers[context->commandBufferIndex]; 800 801 VkCommandBufferBeginInfo beginInfo = { 0 }; 802 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; 803 beginInfo.flags = 0; 804 context->vkBeginCommandBuffer(commandBuffer, &beginInfo); 805 806 VkImageMemoryBarrier2 barrier = { 0 }; 807 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; 808 barrier.srcAccessMask = VK_ACCESS_2_NONE; 809 barrier.dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT; 810 barrier.oldLayout = pVkFrame->layout[0]; 811 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; 812 barrier.image = pVkFrame->img[0]; 813 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 814 barrier.subresourceRange.levelCount = 1; 815 barrier.subresourceRange.layerCount = 1; 816 barrier.srcQueueFamilyIndex = pVkFrame->queue_family[0]; 817 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; 818 barrier.srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; 819 barrier.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; 820 821 VkDependencyInfo dep = { 0 }; 822 dep.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; 823 dep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; 824 dep.imageMemoryBarrierCount = 1; 825 dep.pImageMemoryBarriers = &barrier; 826 context->vkCmdPipelineBarrier2(commandBuffer, &dep); 827 828 context->vkEndCommandBuffer(commandBuffer); 829 830 // Add the image barrier to the submit info 831 submitInfo.commandBufferCount = 1; 832 submitInfo.pCommandBuffers = &context->commandBuffers[context->commandBufferIndex]; 833 834 pVkFrame->layout[0] = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; 835 pVkFrame->queue_family[0] = VK_QUEUE_FAMILY_IGNORED; 836 } 837 838 VkResult result = context->vkQueueSubmit(context->graphicsQueue, 1, &submitInfo, 0); 839 if (result != VK_SUCCESS) { 840 // Don't return an error here, we need to complete the frame operation 841 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION , "vkQueueSubmit(): %s", getVulkanResultString(result)); 842 } 843 844 SDL_AddVulkanRenderSemaphores(renderer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, (Sint64)context->waitSemaphores[context->commandBufferIndex], (Sint64)context->signalSemaphores[context->commandBufferIndex]); 845 846 return 0; 847} 848 849int FinishVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer) 850{ 851 AVHWFramesContext *frames = (AVHWFramesContext *)(frame->hw_frames_ctx->data); 852 AVVulkanFramesContext *vk = (AVVulkanFramesContext *)(frames->hwctx); 853 AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0]; 854 855 // Transition the frame back to ffmpeg 856 ++pVkFrame->sem_value[0]; 857 858 VkTimelineSemaphoreSubmitInfo timeline = { 0 }; 859 timeline.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO; 860 timeline.signalSemaphoreValueCount = 1; 861 timeline.pSignalSemaphoreValues = pVkFrame->sem_value; 862 863 VkPipelineStageFlags pipelineStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; 864 VkSubmitInfo submitInfo = { 0 }; 865 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; 866 submitInfo.waitSemaphoreCount = 1; 867 submitInfo.pWaitSemaphores = &context->signalSemaphores[context->commandBufferIndex]; 868 submitInfo.pWaitDstStageMask = &pipelineStageMask; 869 submitInfo.signalSemaphoreCount = 1; 870 submitInfo.pSignalSemaphores = pVkFrame->sem; 871 submitInfo.pNext = &timeline; 872 873 VkResult result = context->vkQueueSubmit(context->graphicsQueue, 1, &submitInfo, 0); 874 if (result != VK_SUCCESS) { 875 // Don't return an error here, we need to complete the frame operation 876 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "vkQueueSubmit(): %s", getVulkanResultString(result)); 877 } 878 879 vk->unlock_frame(frames, pVkFrame); 880 881 context->commandBufferIndex = (context->commandBufferIndex + 1) % context->commandBufferCount; 882 883 return 0; 884} 885 886SDL_Texture *CreateVulkanVideoTexture(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer, SDL_PropertiesID props) 887{ 888 AVHWFramesContext *frames = (AVHWFramesContext *)(frame->hw_frames_ctx->data); 889 AVVulkanFramesContext *vk = (AVVulkanFramesContext *)(frames->hwctx); 890 AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0]; 891 Uint32 format; 892 893 switch (vk->format[0]) { 894 case VK_FORMAT_G8B8G8R8_422_UNORM: 895 format = SDL_PIXELFORMAT_YUY2; 896 break; 897 case VK_FORMAT_B8G8R8G8_422_UNORM: 898 format = SDL_PIXELFORMAT_UYVY; 899 break; 900 case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM: 901 format = SDL_PIXELFORMAT_IYUV; 902 break; 903 case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM: 904 format = SDL_PIXELFORMAT_NV12; 905 break; 906 case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16: 907 format = SDL_PIXELFORMAT_P010; 908 break; 909 default: 910 format = SDL_PIXELFORMAT_UNKNOWN; 911 break; 912 } 913 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format); 914 SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER, (Sint64)pVkFrame->img[0]); 915 return SDL_CreateTextureWithProperties(renderer, props); 916} 917 918void DestroyVulkanVideoContext(VulkanVideoContext *context) 919{ 920 if (context) { 921 if (context->device) { 922 context->vkDeviceWaitIdle(context->device); 923 } 924 if (context->instanceExtensions) { 925 SDL_free(context->instanceExtensions); 926 } 927 if (context->deviceExtensions) { 928 SDL_free(context->deviceExtensions); 929 } 930 if (context->waitSemaphores) { 931 for (uint32_t i = 0; i < context->waitSemaphoreCount; ++i) { 932 context->vkDestroySemaphore(context->device, context->waitSemaphores[i], NULL); 933 } 934 SDL_free(context->waitSemaphores); 935 context->waitSemaphores = NULL; 936 } 937 if (context->signalSemaphores) { 938 for (uint32_t i = 0; i < context->signalSemaphoreCount; ++i) { 939 context->vkDestroySemaphore(context->device, context->signalSemaphores[i], NULL); 940 } 941 SDL_free(context->signalSemaphores); 942 context->signalSemaphores = NULL; 943 } 944 if (context->commandBuffers) { 945 context->vkFreeCommandBuffers(context->device, context->commandPool, context->commandBufferCount, context->commandBuffers); 946 SDL_free(context->commandBuffers); 947 context->commandBuffers = NULL; 948 } 949 if (context->commandPool) { 950 context->vkDestroyCommandPool(context->device, context->commandPool, NULL); 951 context->commandPool = VK_NULL_HANDLE; 952 } 953 if (context->device) { 954 context->vkDestroyDevice(context->device, NULL); 955 } 956 if (context->surface) { 957 context->vkDestroySurfaceKHR(context->instance, context->surface, NULL); 958 } 959 if (context->instance) { 960 context->vkDestroyInstance(context->instance, NULL); 961 } 962 SDL_free(context); 963 } 964} 965 966#else 967 968VulkanVideoContext *CreateVulkanVideoContext(SDL_Window *window) 969{ 970 SDL_SetError("testffmpeg not built with Vulkan support"); 971 return NULL; 972} 973 974void SetupVulkanRenderProperties(VulkanVideoContext *context, SDL_PropertiesID props) 975{ 976} 977 978void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceContext *ctx) 979{ 980} 981 982SDL_Texture *CreateVulkanVideoTexture(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer, SDL_PropertiesID props) 983{ 984 return NULL; 985} 986 987int BeginVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer) 988{ 989 return -1; 990} 991 992int FinishVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer) 993{ 994 return -1; 995} 996 997void DestroyVulkanVideoContext(VulkanVideoContext *context) 998{ 999} 1000 1001#endif // FFMPEG_VULKAN_SUPPORT