Atlas - testffmpeg_vulkan.c

Home / ext / SDL / test Lines: 1 | Size: 45411 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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_INT >= AV_VERSION_INT(59, 34, 100) 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_INT */ 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_INT >= AV_VERSION_INT(59, 34, 100) 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_INT */ 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 SDL_free(context->instanceExtensions); 942 SDL_free(context->deviceExtensions); 943 if (context->waitSemaphores) { 944 for (uint32_t i = 0; i < context->waitSemaphoreCount; ++i) { 945 context->vkDestroySemaphore(context->device, context->waitSemaphores[i], NULL); 946 } 947 SDL_free(context->waitSemaphores); 948 context->waitSemaphores = NULL; 949 } 950 if (context->signalSemaphores) { 951 for (uint32_t i = 0; i < context->signalSemaphoreCount; ++i) { 952 context->vkDestroySemaphore(context->device, context->signalSemaphores[i], NULL); 953 } 954 SDL_free(context->signalSemaphores); 955 context->signalSemaphores = NULL; 956 } 957 if (context->commandBuffers) { 958 context->vkFreeCommandBuffers(context->device, context->commandPool, context->commandBufferCount, context->commandBuffers); 959 SDL_free(context->commandBuffers); 960 context->commandBuffers = NULL; 961 } 962 if (context->commandPool) { 963 context->vkDestroyCommandPool(context->device, context->commandPool, NULL); 964 context->commandPool = VK_NULL_HANDLE; 965 } 966 if (context->device) { 967 context->vkDestroyDevice(context->device, NULL); 968 } 969 if (context->surface) { 970 context->vkDestroySurfaceKHR(context->instance, context->surface, NULL); 971 } 972 if (context->instance) { 973 context->vkDestroyInstance(context->instance, NULL); 974 } 975 SDL_free(context); 976 } 977} 978 979#else 980 981VulkanVideoContext *CreateVulkanVideoContext(SDL_Window *window) 982{ 983 SDL_SetError("testffmpeg not built with Vulkan support"); 984 return NULL; 985} 986 987void SetupVulkanRenderProperties(VulkanVideoContext *context, SDL_PropertiesID props) 988{ 989} 990 991void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceContext *ctx) 992{ 993} 994 995SDL_Texture *CreateVulkanVideoTexture(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer, SDL_PropertiesID props) 996{ 997 return NULL; 998} 999 1000int BeginVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer) 1001{ 1002 return -1; 1003} 1004 1005int FinishVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer) 1006{ 1007 return -1; 1008} 1009 1010void DestroyVulkanVideoContext(VulkanVideoContext *context) 1011{ 1012} 1013 1014#endif /* FFMPEG_VULKAN_SUPPORT */ 1015
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.