diff options
Diffstat (limited to 'contrib/SDL-3.2.8/test/testffmpeg_vulkan.c')
| -rw-r--r-- | contrib/SDL-3.2.8/test/testffmpeg_vulkan.c | 1001 |
1 files changed, 1001 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/test/testffmpeg_vulkan.c b/contrib/SDL-3.2.8/test/testffmpeg_vulkan.c new file mode 100644 index 0000000..4f2ba81 --- /dev/null +++ b/contrib/SDL-3.2.8/test/testffmpeg_vulkan.c | |||
| @@ -0,0 +1,1001 @@ | |||
| 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 | \ | ||
| 47 | VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceVideoFormatPropertiesKHR) \ | ||
| 48 | |||
| 49 | typedef 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 | |||
| 60 | struct 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 | |||
| 105 | static 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 | |||
| 126 | static 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 | |||
| 142 | static 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 | |||
| 160 | static 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 | |||
| 199 | static 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 | |||
| 254 | static 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 | ||
| 264 | static 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 | |||
| 292 | static 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 | |||
| 446 | static 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 | |||
| 464 | static 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 | |||
| 504 | static 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 | |||
| 547 | static 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 | |||
| 643 | done: | ||
| 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 | |||
| 655 | VulkanVideoContext *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 | |||
| 672 | void 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 | void 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 | |||
| 705 | static 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 | |||
| 771 | int 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 | |||
| 849 | int 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 | |||
| 886 | SDL_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 | |||
| 918 | void 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 | |||
| 968 | VulkanVideoContext *CreateVulkanVideoContext(SDL_Window *window) | ||
| 969 | { | ||
| 970 | SDL_SetError("testffmpeg not built with Vulkan support"); | ||
| 971 | return NULL; | ||
| 972 | } | ||
| 973 | |||
| 974 | void SetupVulkanRenderProperties(VulkanVideoContext *context, SDL_PropertiesID props) | ||
| 975 | { | ||
| 976 | } | ||
| 977 | |||
| 978 | void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceContext *ctx) | ||
| 979 | { | ||
| 980 | } | ||
| 981 | |||
| 982 | SDL_Texture *CreateVulkanVideoTexture(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer, SDL_PropertiesID props) | ||
| 983 | { | ||
| 984 | return NULL; | ||
| 985 | } | ||
| 986 | |||
| 987 | int BeginVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer) | ||
| 988 | { | ||
| 989 | return -1; | ||
| 990 | } | ||
| 991 | |||
| 992 | int FinishVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer) | ||
| 993 | { | ||
| 994 | return -1; | ||
| 995 | } | ||
| 996 | |||
| 997 | void DestroyVulkanVideoContext(VulkanVideoContext *context) | ||
| 998 | { | ||
| 999 | } | ||
| 1000 | |||
| 1001 | #endif // FFMPEG_VULKAN_SUPPORT | ||
