mirror of
				https://github.com/libsdl-org/SDL.git
				synced 2025-11-04 01:34:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1005 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1005 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
  Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
 | 
						|
 | 
						|
  This software is provided 'as-is', without any express or implied
 | 
						|
  warranty.  In no event will the authors be held liable for any damages
 | 
						|
  arising from the use of this software.
 | 
						|
 | 
						|
  Permission is granted to anyone to use this software for any purpose,
 | 
						|
  including commercial applications, and to alter it and redistribute it
 | 
						|
  freely.
 | 
						|
*/
 | 
						|
#include <SDL3/SDL.h>
 | 
						|
#include <SDL3/SDL_vulkan.h>
 | 
						|
 | 
						|
#include "testffmpeg_vulkan.h"
 | 
						|
 | 
						|
#ifdef FFMPEG_VULKAN_SUPPORT
 | 
						|
 | 
						|
#define VULKAN_FUNCTIONS()                                             \
 | 
						|
    VULKAN_GLOBAL_FUNCTION(vkCreateInstance)                           \
 | 
						|
    VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceExtensionProperties)     \
 | 
						|
    VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceLayerProperties)         \
 | 
						|
    VULKAN_INSTANCE_FUNCTION(vkCreateDevice)                           \
 | 
						|
    VULKAN_INSTANCE_FUNCTION(vkDestroyInstance)                        \
 | 
						|
    VULKAN_INSTANCE_FUNCTION(vkDestroySurfaceKHR)                      \
 | 
						|
    VULKAN_INSTANCE_FUNCTION(vkEnumerateDeviceExtensionProperties)     \
 | 
						|
    VULKAN_INSTANCE_FUNCTION(vkEnumeratePhysicalDevices)               \
 | 
						|
    VULKAN_INSTANCE_FUNCTION(vkGetDeviceProcAddr)                      \
 | 
						|
    VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures2)             \
 | 
						|
    VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties) \
 | 
						|
    VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR)     \
 | 
						|
    VULKAN_INSTANCE_FUNCTION(vkQueueWaitIdle)                          \
 | 
						|
    VULKAN_DEVICE_FUNCTION(vkAllocateCommandBuffers)                   \
 | 
						|
    VULKAN_DEVICE_FUNCTION(vkBeginCommandBuffer)                       \
 | 
						|
    VULKAN_DEVICE_FUNCTION(vkCmdPipelineBarrier2)                      \
 | 
						|
    VULKAN_DEVICE_FUNCTION(vkCreateCommandPool)                        \
 | 
						|
    VULKAN_DEVICE_FUNCTION(vkCreateSemaphore)                          \
 | 
						|
    VULKAN_DEVICE_FUNCTION(vkDestroyCommandPool)                       \
 | 
						|
    VULKAN_DEVICE_FUNCTION(vkDestroyDevice)                            \
 | 
						|
    VULKAN_DEVICE_FUNCTION(vkDestroySemaphore)                         \
 | 
						|
    VULKAN_DEVICE_FUNCTION(vkDeviceWaitIdle)                           \
 | 
						|
    VULKAN_DEVICE_FUNCTION(vkEndCommandBuffer)                         \
 | 
						|
    VULKAN_DEVICE_FUNCTION(vkFreeCommandBuffers)                       \
 | 
						|
    VULKAN_DEVICE_FUNCTION(vkGetDeviceQueue)                           \
 | 
						|
    VULKAN_DEVICE_FUNCTION(vkQueueSubmit)                              \
 | 
						|
\
 | 
						|
VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceVideoFormatPropertiesKHR) \
 | 
						|
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
    VkPhysicalDeviceFeatures2 device_features;
 | 
						|
    VkPhysicalDeviceVulkan11Features device_features_1_1;
 | 
						|
    VkPhysicalDeviceVulkan12Features device_features_1_2;
 | 
						|
    VkPhysicalDeviceVulkan13Features device_features_1_3;
 | 
						|
    VkPhysicalDeviceDescriptorBufferFeaturesEXT desc_buf_features;
 | 
						|
    VkPhysicalDeviceShaderAtomicFloatFeaturesEXT atomic_float_features;
 | 
						|
    VkPhysicalDeviceCooperativeMatrixFeaturesKHR coop_matrix_features;
 | 
						|
} VulkanDeviceFeatures;
 | 
						|
 | 
						|
struct VulkanVideoContext
 | 
						|
{
 | 
						|
    VkInstance instance;
 | 
						|
    VkSurfaceKHR surface;
 | 
						|
    VkPhysicalDevice physicalDevice;
 | 
						|
    int presentQueueFamilyIndex;
 | 
						|
    int presentQueueCount;
 | 
						|
    int graphicsQueueFamilyIndex;
 | 
						|
    int graphicsQueueCount;
 | 
						|
    int transferQueueFamilyIndex;
 | 
						|
    int transferQueueCount;
 | 
						|
    int computeQueueFamilyIndex;
 | 
						|
    int computeQueueCount;
 | 
						|
    int decodeQueueFamilyIndex;
 | 
						|
    int decodeQueueCount;
 | 
						|
    VkDevice device;
 | 
						|
    VkQueue graphicsQueue;
 | 
						|
    VkCommandPool commandPool;
 | 
						|
    VkCommandBuffer *commandBuffers;
 | 
						|
    uint32_t commandBufferCount;
 | 
						|
    uint32_t commandBufferIndex;
 | 
						|
    VkSemaphore *waitSemaphores;
 | 
						|
    uint32_t waitSemaphoreCount;
 | 
						|
    VkSemaphore *signalSemaphores;
 | 
						|
    uint32_t signalSemaphoreCount;
 | 
						|
 | 
						|
    const char **instanceExtensions;
 | 
						|
    int instanceExtensionsCount;
 | 
						|
 | 
						|
    const char **deviceExtensions;
 | 
						|
    int deviceExtensionsCount;
 | 
						|
 | 
						|
    VulkanDeviceFeatures features;
 | 
						|
 | 
						|
	PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
 | 
						|
#define VULKAN_GLOBAL_FUNCTION(name)   PFN_##name name;
 | 
						|
#define VULKAN_INSTANCE_FUNCTION(name) PFN_##name name;
 | 
						|
#define VULKAN_DEVICE_FUNCTION(name)   PFN_##name name;
 | 
						|
	VULKAN_FUNCTIONS()
 | 
						|
#undef VULKAN_GLOBAL_FUNCTION
 | 
						|
#undef VULKAN_INSTANCE_FUNCTION
 | 
						|
#undef VULKAN_DEVICE_FUNCTION
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
static int loadGlobalFunctions(VulkanVideoContext *context)
 | 
						|
{
 | 
						|
    context->vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr();
 | 
						|
    if (!context->vkGetInstanceProcAddr) {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
#define VULKAN_GLOBAL_FUNCTION(name)                                                        \
 | 
						|
    context->name = (PFN_##name)context->vkGetInstanceProcAddr(VK_NULL_HANDLE, #name);      \
 | 
						|
    if (!context->name) {                                                                   \
 | 
						|
        return SDL_SetError("vkGetInstanceProcAddr(VK_NULL_HANDLE, \"" #name "\") failed"); \
 | 
						|
    }
 | 
						|
#define VULKAN_INSTANCE_FUNCTION(name)
 | 
						|
#define VULKAN_DEVICE_FUNCTION(name)
 | 
						|
    VULKAN_FUNCTIONS()
 | 
						|
#undef VULKAN_GLOBAL_FUNCTION
 | 
						|
#undef VULKAN_INSTANCE_FUNCTION
 | 
						|
#undef VULKAN_DEVICE_FUNCTION
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int loadInstanceFunctions(VulkanVideoContext *context)
 | 
						|
{
 | 
						|
#define VULKAN_GLOBAL_FUNCTION(name)
 | 
						|
#define VULKAN_INSTANCE_FUNCTION(name)                                                    \
 | 
						|
    context->name = (PFN_##name)context->vkGetInstanceProcAddr(context->instance, #name); \
 | 
						|
    if (!context->name) {                                                                 \
 | 
						|
        return SDL_SetError("vkGetInstanceProcAddr(instance, \"" #name "\") failed");     \
 | 
						|
    }
 | 
						|
#define VULKAN_DEVICE_FUNCTION(name)
 | 
						|
    VULKAN_FUNCTIONS()
 | 
						|
#undef VULKAN_GLOBAL_FUNCTION
 | 
						|
#undef VULKAN_INSTANCE_FUNCTION
 | 
						|
#undef VULKAN_DEVICE_FUNCTION
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int loadDeviceFunctions(VulkanVideoContext *context)
 | 
						|
{
 | 
						|
#define VULKAN_GLOBAL_FUNCTION(name)
 | 
						|
#define VULKAN_INSTANCE_FUNCTION(name)
 | 
						|
#define VULKAN_DEVICE_FUNCTION(name)                                                  \
 | 
						|
    context->name = (PFN_##name)context->vkGetDeviceProcAddr(context->device, #name); \
 | 
						|
    if (!context->name) {                                                             \
 | 
						|
        return SDL_SetError("vkGetDeviceProcAddr(device, \"" #name "\") failed\n");   \
 | 
						|
    }
 | 
						|
    VULKAN_FUNCTIONS()
 | 
						|
#undef VULKAN_GLOBAL_FUNCTION
 | 
						|
#undef VULKAN_INSTANCE_FUNCTION
 | 
						|
#undef VULKAN_DEVICE_FUNCTION
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
#undef VULKAN_FUNCTIONS
 | 
						|
 | 
						|
static const char *getVulkanResultString(VkResult result)
 | 
						|
{
 | 
						|
    switch ((int)result) {
 | 
						|
#define RESULT_CASE(x) \
 | 
						|
    case x:            \
 | 
						|
        return #x
 | 
						|
        RESULT_CASE(VK_SUCCESS);
 | 
						|
        RESULT_CASE(VK_NOT_READY);
 | 
						|
        RESULT_CASE(VK_TIMEOUT);
 | 
						|
        RESULT_CASE(VK_EVENT_SET);
 | 
						|
        RESULT_CASE(VK_EVENT_RESET);
 | 
						|
        RESULT_CASE(VK_INCOMPLETE);
 | 
						|
        RESULT_CASE(VK_ERROR_OUT_OF_HOST_MEMORY);
 | 
						|
        RESULT_CASE(VK_ERROR_OUT_OF_DEVICE_MEMORY);
 | 
						|
        RESULT_CASE(VK_ERROR_INITIALIZATION_FAILED);
 | 
						|
        RESULT_CASE(VK_ERROR_DEVICE_LOST);
 | 
						|
        RESULT_CASE(VK_ERROR_MEMORY_MAP_FAILED);
 | 
						|
        RESULT_CASE(VK_ERROR_LAYER_NOT_PRESENT);
 | 
						|
        RESULT_CASE(VK_ERROR_EXTENSION_NOT_PRESENT);
 | 
						|
        RESULT_CASE(VK_ERROR_FEATURE_NOT_PRESENT);
 | 
						|
        RESULT_CASE(VK_ERROR_INCOMPATIBLE_DRIVER);
 | 
						|
        RESULT_CASE(VK_ERROR_TOO_MANY_OBJECTS);
 | 
						|
        RESULT_CASE(VK_ERROR_FORMAT_NOT_SUPPORTED);
 | 
						|
        RESULT_CASE(VK_ERROR_FRAGMENTED_POOL);
 | 
						|
        RESULT_CASE(VK_ERROR_SURFACE_LOST_KHR);
 | 
						|
        RESULT_CASE(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR);
 | 
						|
        RESULT_CASE(VK_SUBOPTIMAL_KHR);
 | 
						|
        RESULT_CASE(VK_ERROR_OUT_OF_DATE_KHR);
 | 
						|
        RESULT_CASE(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR);
 | 
						|
        RESULT_CASE(VK_ERROR_VALIDATION_FAILED_EXT);
 | 
						|
        RESULT_CASE(VK_ERROR_OUT_OF_POOL_MEMORY_KHR);
 | 
						|
        RESULT_CASE(VK_ERROR_INVALID_SHADER_NV);
 | 
						|
#undef RESULT_CASE
 | 
						|
    default:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    return (result < 0) ? "VK_ERROR_<Unknown>" : "VK_<Unknown>";
 | 
						|
}
 | 
						|
 | 
						|
static int createInstance(VulkanVideoContext *context)
 | 
						|
{
 | 
						|
    static const char *optional_extensions[] = {
 | 
						|
        VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME,
 | 
						|
        VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME
 | 
						|
    };
 | 
						|
    VkApplicationInfo appInfo = { 0 };
 | 
						|
    VkInstanceCreateInfo instanceCreateInfo = { 0 };
 | 
						|
    VkResult result;
 | 
						|
    char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount);
 | 
						|
 | 
						|
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
 | 
						|
    appInfo.apiVersion = VK_API_VERSION_1_3;
 | 
						|
    instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
 | 
						|
    instanceCreateInfo.pApplicationInfo = &appInfo;
 | 
						|
 | 
						|
    const char **instanceExtensionsCopy = SDL_calloc(instanceCreateInfo.enabledExtensionCount + SDL_arraysize(optional_extensions), sizeof(const char *));
 | 
						|
    for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) {
 | 
						|
        instanceExtensionsCopy[i] = instanceExtensions[i];
 | 
						|
    }
 | 
						|
 | 
						|
    // Get the rest of the optional extensions
 | 
						|
    {
 | 
						|
        uint32_t extensionCount;
 | 
						|
        if (context->vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, NULL) == VK_SUCCESS && extensionCount > 0) {
 | 
						|
            VkExtensionProperties *extensionProperties = SDL_calloc(sizeof(VkExtensionProperties), extensionCount);
 | 
						|
            if (context->vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensionProperties) == VK_SUCCESS) {
 | 
						|
                for (uint32_t i = 0; i < SDL_arraysize(optional_extensions); ++i) {
 | 
						|
                    for (uint32_t j = 0; j < extensionCount; ++j) {
 | 
						|
                        if (SDL_strcmp(extensionProperties[j].extensionName, optional_extensions[i]) == 0) {
 | 
						|
                            instanceExtensionsCopy[instanceCreateInfo.enabledExtensionCount++] = optional_extensions[i];
 | 
						|
                            break;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            SDL_free(extensionProperties);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    instanceCreateInfo.ppEnabledExtensionNames = instanceExtensionsCopy;
 | 
						|
 | 
						|
    context->instanceExtensions = instanceExtensionsCopy;
 | 
						|
    context->instanceExtensionsCount = instanceCreateInfo.enabledExtensionCount;
 | 
						|
 | 
						|
    result = context->vkCreateInstance(&instanceCreateInfo, NULL, &context->instance);
 | 
						|
    if (result != VK_SUCCESS) {
 | 
						|
        context->instance = VK_NULL_HANDLE;
 | 
						|
        return SDL_SetError("vkCreateInstance(): %s\n", getVulkanResultString(result));
 | 
						|
    }
 | 
						|
    if (loadInstanceFunctions(context) < 0) {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int createSurface(VulkanVideoContext *context, SDL_Window *window)
 | 
						|
{
 | 
						|
    if (!SDL_Vulkan_CreateSurface(window,
 | 
						|
                                  context->instance,
 | 
						|
                                  NULL,
 | 
						|
                                  &context->surface)) {
 | 
						|
        context->surface = VK_NULL_HANDLE;
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
// Use the same queue scoring algorithm as ffmpeg to make sure we get the same device configuration
 | 
						|
static int selectQueueFamily(VkQueueFamilyProperties *queueFamiliesProperties, uint32_t queueFamiliesCount, VkQueueFlagBits flags, int *queueCount)
 | 
						|
{
 | 
						|
    uint32_t queueFamilyIndex;
 | 
						|
    uint32_t selectedQueueFamilyIndex = queueFamiliesCount;
 | 
						|
    uint32_t min_score = ~0u;
 | 
						|
 | 
						|
    for (queueFamilyIndex = 0; queueFamilyIndex < queueFamiliesCount; ++queueFamilyIndex) {
 | 
						|
        VkQueueFlagBits current_flags = queueFamiliesProperties[queueFamilyIndex].queueFlags;
 | 
						|
        if (current_flags & flags) {
 | 
						|
            uint32_t score = av_popcount(current_flags) + queueFamiliesProperties[queueFamilyIndex].timestampValidBits;
 | 
						|
            if (score < min_score) {
 | 
						|
                selectedQueueFamilyIndex = queueFamilyIndex;
 | 
						|
                min_score = score;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (selectedQueueFamilyIndex != queueFamiliesCount) {
 | 
						|
        VkQueueFamilyProperties *selectedQueueFamily = &queueFamiliesProperties[selectedQueueFamilyIndex];
 | 
						|
        *queueCount = (int)selectedQueueFamily->queueCount;
 | 
						|
        ++selectedQueueFamily->timestampValidBits;
 | 
						|
        return (int)selectedQueueFamilyIndex;
 | 
						|
    } else {
 | 
						|
        *queueCount = 0;
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int findPhysicalDevice(VulkanVideoContext *context)
 | 
						|
{
 | 
						|
    uint32_t physicalDeviceCount = 0;
 | 
						|
    VkPhysicalDevice *physicalDevices;
 | 
						|
    VkQueueFamilyProperties *queueFamiliesProperties = NULL;
 | 
						|
    uint32_t queueFamiliesPropertiesAllocatedSize = 0;
 | 
						|
    VkExtensionProperties *deviceExtensions = NULL;
 | 
						|
    uint32_t deviceExtensionsAllocatedSize = 0;
 | 
						|
    uint32_t physicalDeviceIndex;
 | 
						|
    VkResult result;
 | 
						|
 | 
						|
    result = context->vkEnumeratePhysicalDevices(context->instance, &physicalDeviceCount, NULL);
 | 
						|
    if (result != VK_SUCCESS) {
 | 
						|
        return SDL_SetError("vkEnumeratePhysicalDevices(): %s", getVulkanResultString(result));
 | 
						|
    }
 | 
						|
    if (physicalDeviceCount == 0) {
 | 
						|
        return SDL_SetError("vkEnumeratePhysicalDevices(): no physical devices");
 | 
						|
    }
 | 
						|
    physicalDevices = (VkPhysicalDevice *)SDL_malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount);
 | 
						|
    if (!physicalDevices) {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    result = context->vkEnumeratePhysicalDevices(context->instance, &physicalDeviceCount, physicalDevices);
 | 
						|
    if (result != VK_SUCCESS) {
 | 
						|
        SDL_free(physicalDevices);
 | 
						|
        return SDL_SetError("vkEnumeratePhysicalDevices(): %s", getVulkanResultString(result));
 | 
						|
    }
 | 
						|
    context->physicalDevice = NULL;
 | 
						|
    for (physicalDeviceIndex = 0; physicalDeviceIndex < physicalDeviceCount; physicalDeviceIndex++) {
 | 
						|
        uint32_t queueFamiliesCount = 0;
 | 
						|
        uint32_t queueFamilyIndex;
 | 
						|
        uint32_t deviceExtensionCount = 0;
 | 
						|
        SDL_bool hasSwapchainExtension = SDL_FALSE;
 | 
						|
        uint32_t i;
 | 
						|
 | 
						|
        VkPhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex];
 | 
						|
        context->vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, NULL);
 | 
						|
        if (queueFamiliesCount == 0) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        if (queueFamiliesPropertiesAllocatedSize < queueFamiliesCount) {
 | 
						|
            SDL_free(queueFamiliesProperties);
 | 
						|
            queueFamiliesPropertiesAllocatedSize = queueFamiliesCount;
 | 
						|
            queueFamiliesProperties = (VkQueueFamilyProperties *)SDL_malloc(sizeof(VkQueueFamilyProperties) * queueFamiliesPropertiesAllocatedSize);
 | 
						|
            if (!queueFamiliesProperties) {
 | 
						|
                SDL_free(physicalDevices);
 | 
						|
                SDL_free(deviceExtensions);
 | 
						|
                return -1;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        context->vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, queueFamiliesProperties);
 | 
						|
 | 
						|
        // Initialize timestampValidBits for scoring in selectQueueFamily
 | 
						|
        for (queueFamilyIndex = 0; queueFamilyIndex < queueFamiliesCount; queueFamilyIndex++) {
 | 
						|
            queueFamiliesProperties[queueFamilyIndex].timestampValidBits = 0;
 | 
						|
        }
 | 
						|
        context->presentQueueFamilyIndex = -1;
 | 
						|
        context->graphicsQueueFamilyIndex = -1;
 | 
						|
        for (queueFamilyIndex = 0; queueFamilyIndex < queueFamiliesCount; queueFamilyIndex++) {
 | 
						|
            VkBool32 supported = 0;
 | 
						|
 | 
						|
            if (queueFamiliesProperties[queueFamilyIndex].queueCount == 0) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            if (queueFamiliesProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
 | 
						|
                context->graphicsQueueFamilyIndex = queueFamilyIndex;
 | 
						|
            }
 | 
						|
 | 
						|
            result = context->vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, context->surface, &supported);
 | 
						|
            if (result == VK_SUCCESS) {
 | 
						|
                if (supported) {
 | 
						|
                    context->presentQueueFamilyIndex = queueFamilyIndex;
 | 
						|
                    if (queueFamiliesProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
 | 
						|
                        break; // use this queue because it can present and do graphics
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (context->presentQueueFamilyIndex < 0 || context->graphicsQueueFamilyIndex < 0) {
 | 
						|
            // We can't render and present on this device
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        context->presentQueueCount = queueFamiliesProperties[context->presentQueueFamilyIndex].queueCount;
 | 
						|
        ++queueFamiliesProperties[context->presentQueueFamilyIndex].timestampValidBits;
 | 
						|
        context->graphicsQueueCount = queueFamiliesProperties[context->graphicsQueueFamilyIndex].queueCount;
 | 
						|
        ++queueFamiliesProperties[context->graphicsQueueFamilyIndex].timestampValidBits;
 | 
						|
 | 
						|
        context->transferQueueFamilyIndex = selectQueueFamily(queueFamiliesProperties, queueFamiliesCount, VK_QUEUE_TRANSFER_BIT, &context->transferQueueCount);
 | 
						|
        context->computeQueueFamilyIndex = selectQueueFamily(queueFamiliesProperties, queueFamiliesCount, VK_QUEUE_COMPUTE_BIT, &context->computeQueueCount);
 | 
						|
        context->decodeQueueFamilyIndex = selectQueueFamily(queueFamiliesProperties, queueFamiliesCount, VK_QUEUE_VIDEO_DECODE_BIT_KHR, &context->decodeQueueCount);
 | 
						|
        if (context->transferQueueFamilyIndex < 0) {
 | 
						|
            // ffmpeg can fall back to the compute or graphics queues for this
 | 
						|
            context->transferQueueFamilyIndex = selectQueueFamily(queueFamiliesProperties, queueFamiliesCount, VK_QUEUE_COMPUTE_BIT, &context->transferQueueCount);
 | 
						|
            if (context->transferQueueFamilyIndex < 0) {
 | 
						|
                context->transferQueueFamilyIndex = selectQueueFamily(queueFamiliesProperties, queueFamiliesCount, VK_QUEUE_GRAPHICS_BIT, &context->transferQueueCount);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (context->transferQueueFamilyIndex < 0 ||
 | 
						|
            context->computeQueueFamilyIndex < 0) {
 | 
						|
            // This device doesn't have the queues we need for video decoding
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        result = context->vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, NULL);
 | 
						|
        if (result != VK_SUCCESS) {
 | 
						|
            SDL_free(physicalDevices);
 | 
						|
            SDL_free(queueFamiliesProperties);
 | 
						|
            SDL_free(deviceExtensions);
 | 
						|
            return SDL_SetError("vkEnumerateDeviceExtensionProperties(): %s", getVulkanResultString(result));
 | 
						|
        }
 | 
						|
        if (deviceExtensionCount == 0) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        if (deviceExtensionsAllocatedSize < deviceExtensionCount) {
 | 
						|
            SDL_free(deviceExtensions);
 | 
						|
            deviceExtensionsAllocatedSize = deviceExtensionCount;
 | 
						|
            deviceExtensions = SDL_malloc(sizeof(VkExtensionProperties) * deviceExtensionsAllocatedSize);
 | 
						|
            if (!deviceExtensions) {
 | 
						|
                SDL_free(physicalDevices);
 | 
						|
                SDL_free(queueFamiliesProperties);
 | 
						|
                return -1;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        result = context->vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, deviceExtensions);
 | 
						|
        if (result != VK_SUCCESS) {
 | 
						|
            SDL_free(physicalDevices);
 | 
						|
            SDL_free(queueFamiliesProperties);
 | 
						|
            SDL_free(deviceExtensions);
 | 
						|
            return SDL_SetError("vkEnumerateDeviceExtensionProperties(): %s", getVulkanResultString(result));
 | 
						|
        }
 | 
						|
        for (i = 0; i < deviceExtensionCount; i++) {
 | 
						|
            if (SDL_strcmp(deviceExtensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0) {
 | 
						|
                hasSwapchainExtension = SDL_TRUE;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (!hasSwapchainExtension) {
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        context->physicalDevice = physicalDevice;
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    SDL_free(physicalDevices);
 | 
						|
    SDL_free(queueFamiliesProperties);
 | 
						|
    SDL_free(deviceExtensions);
 | 
						|
    if (!context->physicalDevice) {
 | 
						|
        return SDL_SetError("Vulkan: no viable physical devices found");
 | 
						|
    }
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void initDeviceFeatures(VulkanDeviceFeatures *features)
 | 
						|
{
 | 
						|
    features->device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
 | 
						|
    features->device_features.pNext = &features->device_features_1_1;
 | 
						|
    features->device_features_1_1.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
 | 
						|
    features->device_features_1_1.pNext = &features->device_features_1_2;
 | 
						|
    features->device_features_1_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
 | 
						|
    features->device_features_1_2.pNext = &features->device_features_1_3;
 | 
						|
    features->device_features_1_3.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
 | 
						|
    features->device_features_1_3.pNext = &features->desc_buf_features;
 | 
						|
    features->desc_buf_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT;
 | 
						|
    features->desc_buf_features.pNext = &features->atomic_float_features;
 | 
						|
    features->atomic_float_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_FEATURES_EXT;
 | 
						|
    features->atomic_float_features.pNext = &features->coop_matrix_features;
 | 
						|
    features->coop_matrix_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_KHR;
 | 
						|
    features->coop_matrix_features.pNext = NULL;
 | 
						|
}
 | 
						|
 | 
						|
static void copyDeviceFeatures(VulkanDeviceFeatures *supported_features, VulkanDeviceFeatures *requested_features)
 | 
						|
{
 | 
						|
#define COPY_OPTIONAL_FEATURE(X) requested_features->X = supported_features->X
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features.features.shaderImageGatherExtended);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features.features.shaderStorageImageReadWithoutFormat);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features.features.shaderStorageImageWriteWithoutFormat);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features.features.fragmentStoresAndAtomics);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features.features.vertexPipelineStoresAndAtomics);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features.features.shaderInt64);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features.features.shaderInt16);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features.features.shaderFloat64);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_1.samplerYcbcrConversion);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_1.storagePushConstant16);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_2.bufferDeviceAddress);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_2.hostQueryReset);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_2.storagePushConstant8);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_2.shaderInt8);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_2.storageBuffer8BitAccess);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_2.uniformAndStorageBuffer8BitAccess);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_2.shaderFloat16);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_2.shaderSharedInt64Atomics);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_2.vulkanMemoryModel);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_2.vulkanMemoryModelDeviceScope);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_2.hostQueryReset);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_3.dynamicRendering);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_3.maintenance4);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_3.synchronization2);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_3.computeFullSubgroups);
 | 
						|
    COPY_OPTIONAL_FEATURE(device_features_1_3.shaderZeroInitializeWorkgroupMemory);
 | 
						|
    COPY_OPTIONAL_FEATURE(desc_buf_features.descriptorBuffer);
 | 
						|
    COPY_OPTIONAL_FEATURE(desc_buf_features.descriptorBufferPushDescriptors);
 | 
						|
    COPY_OPTIONAL_FEATURE(atomic_float_features.shaderBufferFloat32Atomics);
 | 
						|
    COPY_OPTIONAL_FEATURE(atomic_float_features.shaderBufferFloat32AtomicAdd);
 | 
						|
    COPY_OPTIONAL_FEATURE(coop_matrix_features.cooperativeMatrix);
 | 
						|
#undef COPY_OPTIONAL_FEATURE
 | 
						|
 | 
						|
    // timeline semaphores is required by ffmpeg
 | 
						|
    requested_features->device_features_1_2.timelineSemaphore = 1;
 | 
						|
}
 | 
						|
 | 
						|
static int addQueueFamily(VkDeviceQueueCreateInfo **pQueueCreateInfos, uint32_t *pQueueCreateInfoCount, uint32_t queueFamilyIndex, uint32_t queueCount)
 | 
						|
{
 | 
						|
    VkDeviceQueueCreateInfo *queueCreateInfo;
 | 
						|
    VkDeviceQueueCreateInfo *queueCreateInfos = *pQueueCreateInfos;
 | 
						|
    uint32_t queueCreateInfoCount = *pQueueCreateInfoCount;
 | 
						|
    float *queuePriorities;
 | 
						|
 | 
						|
    if (queueCount == 0) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    for (uint32_t i = 0; i < queueCreateInfoCount; ++i) {
 | 
						|
        if (queueCreateInfos[i].queueFamilyIndex == queueFamilyIndex) {
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    queueCreateInfos = (VkDeviceQueueCreateInfo *)SDL_realloc(queueCreateInfos, (queueCreateInfoCount + 1) * sizeof(*queueCreateInfos));
 | 
						|
    if (!queueCreateInfos) {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    queuePriorities = (float *)SDL_malloc(queueCount * sizeof(*queuePriorities));
 | 
						|
    if (!queuePriorities) {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    for (uint32_t i = 0; i < queueCount; ++i) {
 | 
						|
        queuePriorities[i] = 1.0f / queueCount;
 | 
						|
    }
 | 
						|
 | 
						|
    queueCreateInfo = &queueCreateInfos[queueCreateInfoCount++];
 | 
						|
    SDL_zerop(queueCreateInfo);
 | 
						|
    queueCreateInfo->sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
 | 
						|
    queueCreateInfo->queueFamilyIndex = queueFamilyIndex;
 | 
						|
    queueCreateInfo->queueCount = queueCount;
 | 
						|
    queueCreateInfo->pQueuePriorities = queuePriorities;
 | 
						|
 | 
						|
    *pQueueCreateInfos = queueCreateInfos;
 | 
						|
    *pQueueCreateInfoCount = queueCreateInfoCount;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int createDevice(VulkanVideoContext *context)
 | 
						|
{
 | 
						|
    static const char *const deviceExtensionNames[] = {
 | 
						|
        VK_KHR_SWAPCHAIN_EXTENSION_NAME,
 | 
						|
        VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
 | 
						|
        VK_KHR_MAINTENANCE1_EXTENSION_NAME,
 | 
						|
        VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
 | 
						|
        VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,
 | 
						|
    };
 | 
						|
    static const char *optional_extensions[] = {
 | 
						|
        VK_KHR_VIDEO_QUEUE_EXTENSION_NAME,
 | 
						|
        VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME,
 | 
						|
        VK_KHR_VIDEO_DECODE_H264_EXTENSION_NAME,
 | 
						|
        VK_KHR_VIDEO_DECODE_H265_EXTENSION_NAME,
 | 
						|
        VK_KHR_VIDEO_DECODE_AV1_EXTENSION_NAME
 | 
						|
    };
 | 
						|
    VkDeviceCreateInfo deviceCreateInfo = { 0 };
 | 
						|
    VkDeviceQueueCreateInfo *queueCreateInfos = NULL;
 | 
						|
    uint32_t queueCreateInfoCount = 0;
 | 
						|
    VulkanDeviceFeatures supported_features;
 | 
						|
    VkResult result = VK_ERROR_UNKNOWN;
 | 
						|
 | 
						|
    if (addQueueFamily(&queueCreateInfos, &queueCreateInfoCount, context->presentQueueFamilyIndex, context->presentQueueCount) < 0 ||
 | 
						|
        addQueueFamily(&queueCreateInfos, &queueCreateInfoCount, context->graphicsQueueFamilyIndex, context->graphicsQueueCount) < 0 ||
 | 
						|
        addQueueFamily(&queueCreateInfos, &queueCreateInfoCount, context->transferQueueFamilyIndex, context->transferQueueCount) < 0 ||
 | 
						|
        addQueueFamily(&queueCreateInfos, &queueCreateInfoCount, context->computeQueueFamilyIndex, context->computeQueueCount) < 0 ||
 | 
						|
        addQueueFamily(&queueCreateInfos, &queueCreateInfoCount, context->decodeQueueFamilyIndex, context->decodeQueueCount) < 0) {
 | 
						|
        goto done;
 | 
						|
    }
 | 
						|
 | 
						|
    initDeviceFeatures(&supported_features);
 | 
						|
    initDeviceFeatures(&context->features);
 | 
						|
    context->vkGetPhysicalDeviceFeatures2(context->physicalDevice, &supported_features.device_features);
 | 
						|
    copyDeviceFeatures(&supported_features, &context->features);
 | 
						|
 | 
						|
    deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
 | 
						|
    deviceCreateInfo.queueCreateInfoCount = queueCreateInfoCount;
 | 
						|
    deviceCreateInfo.pQueueCreateInfos = queueCreateInfos;
 | 
						|
    deviceCreateInfo.pEnabledFeatures = NULL;
 | 
						|
    deviceCreateInfo.enabledExtensionCount = SDL_arraysize(deviceExtensionNames);
 | 
						|
    deviceCreateInfo.pNext = &context->features.device_features;
 | 
						|
 | 
						|
    const char **deviceExtensionsCopy = SDL_calloc(deviceCreateInfo.enabledExtensionCount + SDL_arraysize(optional_extensions), sizeof(const char *));
 | 
						|
    for (uint32_t i = 0; i < deviceCreateInfo.enabledExtensionCount; i++) {
 | 
						|
        deviceExtensionsCopy[i] = deviceExtensionNames[i];
 | 
						|
    }
 | 
						|
 | 
						|
    // Get the rest of the optional extensions
 | 
						|
    {
 | 
						|
        uint32_t extensionCount;
 | 
						|
        if (context->vkEnumerateDeviceExtensionProperties(context->physicalDevice, NULL, &extensionCount, NULL) == VK_SUCCESS && extensionCount > 0) {
 | 
						|
            VkExtensionProperties *extensionProperties = SDL_calloc(sizeof(VkExtensionProperties), extensionCount);
 | 
						|
            if (context->vkEnumerateDeviceExtensionProperties(context->physicalDevice, NULL, &extensionCount, extensionProperties) == VK_SUCCESS) {
 | 
						|
                for (uint32_t i = 0; i < SDL_arraysize(optional_extensions); ++i) {
 | 
						|
                    for (uint32_t j = 0; j < extensionCount; ++j) {
 | 
						|
                        if (SDL_strcmp(extensionProperties[j].extensionName, optional_extensions[i]) == 0) {
 | 
						|
                            deviceExtensionsCopy[deviceCreateInfo.enabledExtensionCount++] = optional_extensions[i];
 | 
						|
                            break;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            SDL_free(extensionProperties);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionsCopy;
 | 
						|
 | 
						|
    context->deviceExtensions = deviceExtensionsCopy;
 | 
						|
    context->deviceExtensionsCount = deviceCreateInfo.enabledExtensionCount;
 | 
						|
 | 
						|
    result = context->vkCreateDevice(context->physicalDevice, &deviceCreateInfo, NULL, &context->device);
 | 
						|
    if (result != VK_SUCCESS) {
 | 
						|
        SDL_SetError("vkCreateDevice(): %s", getVulkanResultString(result));
 | 
						|
        goto done;
 | 
						|
    }
 | 
						|
 | 
						|
    if (loadDeviceFunctions(context) < 0) {
 | 
						|
        result = VK_ERROR_UNKNOWN;
 | 
						|
        context->device = VK_NULL_HANDLE;
 | 
						|
        goto done;
 | 
						|
    }
 | 
						|
 | 
						|
    // Get the graphics queue that SDL will use
 | 
						|
    context->vkGetDeviceQueue(context->device, context->graphicsQueueFamilyIndex, 0, &context->graphicsQueue);
 | 
						|
 | 
						|
    // Create a command pool
 | 
						|
    VkCommandPoolCreateInfo commandPoolCreateInfo = { 0 };
 | 
						|
    commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
 | 
						|
    commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
 | 
						|
    commandPoolCreateInfo.queueFamilyIndex = context->graphicsQueueFamilyIndex;
 | 
						|
    result = context->vkCreateCommandPool(context->device, &commandPoolCreateInfo, NULL, &context->commandPool);
 | 
						|
    if (result != VK_SUCCESS) {
 | 
						|
        SDL_SetError("vkCreateCommandPool(): %s", getVulkanResultString(result));
 | 
						|
        goto done;
 | 
						|
    }
 | 
						|
 | 
						|
done:
 | 
						|
    for (uint32_t i = 0; i < queueCreateInfoCount; ++i) {
 | 
						|
        SDL_free((void *)queueCreateInfos[i].pQueuePriorities);
 | 
						|
    }
 | 
						|
    SDL_free(queueCreateInfos);
 | 
						|
 | 
						|
    if (result != VK_SUCCESS) {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
VulkanVideoContext *CreateVulkanVideoContext(SDL_Window *window)
 | 
						|
{
 | 
						|
	VulkanVideoContext *context = SDL_calloc(1, sizeof(*context));
 | 
						|
	if (!context) {
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
    if (loadGlobalFunctions(context) < 0 ||
 | 
						|
        createInstance(context) < 0 ||
 | 
						|
        createSurface(context, window) < 0 ||
 | 
						|
        findPhysicalDevice(context) < 0 ||
 | 
						|
        createDevice(context) < 0) {
 | 
						|
		DestroyVulkanVideoContext(context);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
	return context;
 | 
						|
}
 | 
						|
 | 
						|
void SetupVulkanRenderProperties(VulkanVideoContext *context, SDL_PropertiesID props)
 | 
						|
{
 | 
						|
    SDL_SetProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER, context->instance);
 | 
						|
    SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_SURFACE_NUMBER, (Sint64)context->surface);
 | 
						|
    SDL_SetProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_PHYSICAL_DEVICE_POINTER, context->physicalDevice);
 | 
						|
    SDL_SetProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER, context->device);
 | 
						|
    SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER, context->presentQueueFamilyIndex);
 | 
						|
    SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER, context->graphicsQueueFamilyIndex);
 | 
						|
}
 | 
						|
 | 
						|
void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceContext *ctx)
 | 
						|
{
 | 
						|
    ctx->get_proc_addr = context->vkGetInstanceProcAddr;
 | 
						|
    ctx->inst = context->instance;
 | 
						|
    ctx->phys_dev = context->physicalDevice;
 | 
						|
    ctx->act_dev = context->device;
 | 
						|
    ctx->device_features = context->features.device_features;
 | 
						|
    ctx->enabled_inst_extensions = context->instanceExtensions;
 | 
						|
    ctx->nb_enabled_inst_extensions = context->instanceExtensionsCount;
 | 
						|
    ctx->enabled_dev_extensions = context->deviceExtensions;
 | 
						|
    ctx->nb_enabled_dev_extensions = context->deviceExtensionsCount;
 | 
						|
    ctx->queue_family_index = context->graphicsQueueFamilyIndex;
 | 
						|
    ctx->nb_graphics_queues = context->graphicsQueueCount;
 | 
						|
    ctx->queue_family_tx_index = context->transferQueueFamilyIndex;
 | 
						|
    ctx->nb_tx_queues = context->transferQueueCount;
 | 
						|
    ctx->queue_family_comp_index = context->computeQueueFamilyIndex;
 | 
						|
    ctx->nb_comp_queues = context->computeQueueCount;
 | 
						|
    ctx->queue_family_encode_index = -1;
 | 
						|
    ctx->nb_encode_queues = 0;
 | 
						|
    ctx->queue_family_decode_index = context->decodeQueueFamilyIndex;
 | 
						|
    ctx->nb_decode_queues = context->decodeQueueCount;
 | 
						|
}
 | 
						|
 | 
						|
static int CreateCommandBuffers(VulkanVideoContext *context, SDL_Renderer *renderer)
 | 
						|
{
 | 
						|
    uint32_t commandBufferCount = (uint32_t)SDL_GetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_VULKAN_SWAPCHAIN_IMAGE_COUNT_NUMBER, 1);
 | 
						|
 | 
						|
    if (commandBufferCount > context->waitSemaphoreCount) {
 | 
						|
        VkSemaphore *semaphores = (VkSemaphore *)SDL_realloc(context->waitSemaphores, commandBufferCount * sizeof(*semaphores));
 | 
						|
        if (!semaphores) {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        context->waitSemaphores = semaphores;
 | 
						|
 | 
						|
        VkSemaphoreCreateInfo semaphoreCreateInfo = { 0 };
 | 
						|
        semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
 | 
						|
        while (context->waitSemaphoreCount < commandBufferCount) {
 | 
						|
            VkResult result = context->vkCreateSemaphore(context->device, &semaphoreCreateInfo, NULL, &context->waitSemaphores[context->waitSemaphoreCount]);
 | 
						|
            if (result != VK_SUCCESS) {
 | 
						|
                SDL_SetError("vkCreateSemaphore(): %s", getVulkanResultString(result));
 | 
						|
                return -1;
 | 
						|
            }
 | 
						|
            ++context->waitSemaphoreCount;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (commandBufferCount > context->signalSemaphoreCount) {
 | 
						|
        VkSemaphore *semaphores = (VkSemaphore *)SDL_realloc(context->signalSemaphores, commandBufferCount * sizeof(*semaphores));
 | 
						|
        if (!semaphores) {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        context->signalSemaphores = semaphores;
 | 
						|
 | 
						|
        VkSemaphoreCreateInfo semaphoreCreateInfo = { 0 };
 | 
						|
        semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
 | 
						|
        while (context->signalSemaphoreCount < commandBufferCount) {
 | 
						|
            VkResult result = context->vkCreateSemaphore(context->device, &semaphoreCreateInfo, NULL, &context->signalSemaphores[context->signalSemaphoreCount]);
 | 
						|
            if (result != VK_SUCCESS) {
 | 
						|
                SDL_SetError("vkCreateSemaphore(): %s", getVulkanResultString(result));
 | 
						|
                return -1;
 | 
						|
            }
 | 
						|
            ++context->signalSemaphoreCount;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (commandBufferCount > context->commandBufferCount) {
 | 
						|
        uint32_t needed = (commandBufferCount - context->commandBufferCount);
 | 
						|
        VkCommandBuffer *commandBuffers = (VkCommandBuffer *)SDL_realloc(context->commandBuffers, commandBufferCount * sizeof(*commandBuffers));
 | 
						|
        if (!commandBuffers) {
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        context->commandBuffers = commandBuffers;
 | 
						|
 | 
						|
        VkCommandBufferAllocateInfo commandBufferAllocateInfo = { 0 };
 | 
						|
        commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
 | 
						|
        commandBufferAllocateInfo.commandPool = context->commandPool;
 | 
						|
        commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
 | 
						|
        commandBufferAllocateInfo.commandBufferCount = needed;
 | 
						|
        VkResult result = context->vkAllocateCommandBuffers(context->device, &commandBufferAllocateInfo, &context->commandBuffers[context->commandBufferCount]);
 | 
						|
        if (result != VK_SUCCESS) {
 | 
						|
            SDL_SetError("vkAllocateCommandBuffers(): %s", getVulkanResultString(result));
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
 | 
						|
        context->commandBufferCount = commandBufferCount;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int BeginVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer)
 | 
						|
{
 | 
						|
    AVHWFramesContext *frames = (AVHWFramesContext *)(frame->hw_frames_ctx->data);
 | 
						|
    AVVulkanFramesContext *vk = (AVVulkanFramesContext *)(frames->hwctx);
 | 
						|
    AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0];
 | 
						|
 | 
						|
    if (CreateCommandBuffers(context, renderer) < 0) {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    vk->lock_frame(frames, pVkFrame);
 | 
						|
 | 
						|
    VkTimelineSemaphoreSubmitInfo timeline = { 0 };
 | 
						|
    timeline.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO;
 | 
						|
    timeline.waitSemaphoreValueCount = 1;
 | 
						|
    timeline.pWaitSemaphoreValues = pVkFrame->sem_value;
 | 
						|
 | 
						|
    VkPipelineStageFlags pipelineStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
 | 
						|
    VkSubmitInfo submitInfo = { 0 };
 | 
						|
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
 | 
						|
    submitInfo.waitSemaphoreCount = 1;
 | 
						|
    submitInfo.pWaitSemaphores = pVkFrame->sem;
 | 
						|
    submitInfo.pWaitDstStageMask = &pipelineStageMask;
 | 
						|
    submitInfo.signalSemaphoreCount = 1;
 | 
						|
    submitInfo.pSignalSemaphores = &context->waitSemaphores[context->commandBufferIndex];
 | 
						|
    submitInfo.pNext = &timeline;
 | 
						|
 | 
						|
    if (pVkFrame->layout[0] != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
 | 
						|
        VkCommandBuffer commandBuffer = context->commandBuffers[context->commandBufferIndex];
 | 
						|
 | 
						|
        VkCommandBufferBeginInfo beginInfo = { 0 };
 | 
						|
        beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
 | 
						|
        beginInfo.flags = 0;
 | 
						|
        context->vkBeginCommandBuffer(commandBuffer, &beginInfo);
 | 
						|
 | 
						|
        VkImageMemoryBarrier2 barrier = { 0 };
 | 
						|
        barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
 | 
						|
        barrier.srcAccessMask = VK_ACCESS_2_NONE;
 | 
						|
        barrier.dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT;
 | 
						|
        barrier.oldLayout = pVkFrame->layout[0];
 | 
						|
        barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
 | 
						|
        barrier.image = pVkFrame->img[0];
 | 
						|
        barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
 | 
						|
        barrier.subresourceRange.levelCount = 1;
 | 
						|
        barrier.subresourceRange.layerCount = 1;
 | 
						|
        barrier.srcQueueFamilyIndex = pVkFrame->queue_family[0];
 | 
						|
        barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
 | 
						|
        barrier.srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
 | 
						|
        barrier.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
 | 
						|
 | 
						|
        VkDependencyInfo dep = { 0 };
 | 
						|
        dep.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
 | 
						|
        dep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
 | 
						|
        dep.imageMemoryBarrierCount = 1;
 | 
						|
        dep.pImageMemoryBarriers = &barrier;
 | 
						|
        context->vkCmdPipelineBarrier2(commandBuffer, &dep);
 | 
						|
 | 
						|
        context->vkEndCommandBuffer(commandBuffer);
 | 
						|
 | 
						|
        // Add the image barrier to the submit info
 | 
						|
        submitInfo.commandBufferCount = 1;
 | 
						|
        submitInfo.pCommandBuffers = &context->commandBuffers[context->commandBufferIndex];
 | 
						|
 | 
						|
        pVkFrame->layout[0] = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
 | 
						|
        pVkFrame->queue_family[0] = VK_QUEUE_FAMILY_IGNORED;
 | 
						|
    }
 | 
						|
 | 
						|
    VkResult result = context->vkQueueSubmit(context->graphicsQueue, 1, &submitInfo, 0);
 | 
						|
    if (result != VK_SUCCESS) {
 | 
						|
        // Don't return an error here, we need to complete the frame operation
 | 
						|
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION , "vkQueueSubmit(): %s", getVulkanResultString(result));
 | 
						|
    }
 | 
						|
 | 
						|
    SDL_AddVulkanRenderSemaphores(renderer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, (Sint64)context->waitSemaphores[context->commandBufferIndex], (Sint64)context->signalSemaphores[context->commandBufferIndex]);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int FinishVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer)
 | 
						|
{
 | 
						|
    AVHWFramesContext *frames = (AVHWFramesContext *)(frame->hw_frames_ctx->data);
 | 
						|
    AVVulkanFramesContext *vk = (AVVulkanFramesContext *)(frames->hwctx);
 | 
						|
    AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0];
 | 
						|
 | 
						|
    // Transition the frame back to ffmpeg
 | 
						|
    ++pVkFrame->sem_value[0];
 | 
						|
 | 
						|
    VkTimelineSemaphoreSubmitInfo timeline = { 0 };
 | 
						|
    timeline.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO;
 | 
						|
    timeline.signalSemaphoreValueCount = 1;
 | 
						|
    timeline.pSignalSemaphoreValues = pVkFrame->sem_value;
 | 
						|
 | 
						|
    VkPipelineStageFlags pipelineStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
 | 
						|
    VkSubmitInfo submitInfo = { 0 };
 | 
						|
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
 | 
						|
    submitInfo.waitSemaphoreCount = 1;
 | 
						|
    submitInfo.pWaitSemaphores = &context->signalSemaphores[context->commandBufferIndex];
 | 
						|
    submitInfo.pWaitDstStageMask = &pipelineStageMask;
 | 
						|
    submitInfo.signalSemaphoreCount = 1;
 | 
						|
    submitInfo.pSignalSemaphores = pVkFrame->sem;
 | 
						|
    submitInfo.pNext = &timeline;
 | 
						|
 | 
						|
    VkResult result = context->vkQueueSubmit(context->graphicsQueue, 1, &submitInfo, 0);
 | 
						|
    if (result != VK_SUCCESS) {
 | 
						|
        // Don't return an error here, we need to complete the frame operation
 | 
						|
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "vkQueueSubmit(): %s", getVulkanResultString(result));
 | 
						|
    }
 | 
						|
 | 
						|
    vk->unlock_frame(frames, pVkFrame);
 | 
						|
 | 
						|
    context->commandBufferIndex = (context->commandBufferIndex + 1) % context->commandBufferCount;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
SDL_Texture *CreateVulkanVideoTexture(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer, SDL_PropertiesID props)
 | 
						|
{
 | 
						|
    AVHWFramesContext *frames = (AVHWFramesContext *)(frame->hw_frames_ctx->data);
 | 
						|
    AVVulkanFramesContext *vk = (AVVulkanFramesContext *)(frames->hwctx);
 | 
						|
    AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0];
 | 
						|
    Uint32 format;
 | 
						|
 | 
						|
    switch (vk->format[0]) {
 | 
						|
    case VK_FORMAT_G8B8G8R8_422_UNORM:
 | 
						|
        format = SDL_PIXELFORMAT_YUY2;
 | 
						|
        break;
 | 
						|
    case VK_FORMAT_B8G8R8G8_422_UNORM:
 | 
						|
        format = SDL_PIXELFORMAT_UYVY;
 | 
						|
        break;
 | 
						|
    case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
 | 
						|
        format = SDL_PIXELFORMAT_IYUV;
 | 
						|
        break;
 | 
						|
    case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
 | 
						|
        format = SDL_PIXELFORMAT_NV12;
 | 
						|
        break;
 | 
						|
    case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
 | 
						|
        format = SDL_PIXELFORMAT_P010;
 | 
						|
        break;
 | 
						|
    default:
 | 
						|
        format = SDL_PIXELFORMAT_UNKNOWN;
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format);
 | 
						|
    SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER, (Sint64)pVkFrame->img[0]);
 | 
						|
    return SDL_CreateTextureWithProperties(renderer, props);
 | 
						|
}
 | 
						|
 | 
						|
void DestroyVulkanVideoContext(VulkanVideoContext *context)
 | 
						|
{
 | 
						|
    if (context) {
 | 
						|
        if (context->device) {
 | 
						|
            context->vkDeviceWaitIdle(context->device);
 | 
						|
        }
 | 
						|
        if (context->instanceExtensions) {
 | 
						|
            SDL_free(context->instanceExtensions);
 | 
						|
        }
 | 
						|
        if (context->deviceExtensions) {
 | 
						|
            SDL_free(context->deviceExtensions);
 | 
						|
        }
 | 
						|
        if (context->waitSemaphores) {
 | 
						|
            for (uint32_t i = 0; i < context->waitSemaphoreCount; ++i) {
 | 
						|
                context->vkDestroySemaphore(context->device, context->waitSemaphores[i], NULL);
 | 
						|
            }
 | 
						|
            SDL_free(context->waitSemaphores);
 | 
						|
            context->waitSemaphores = NULL;
 | 
						|
        }
 | 
						|
        if (context->signalSemaphores) {
 | 
						|
            for (uint32_t i = 0; i < context->signalSemaphoreCount; ++i) {
 | 
						|
                context->vkDestroySemaphore(context->device, context->signalSemaphores[i], NULL);
 | 
						|
            }
 | 
						|
            SDL_free(context->signalSemaphores);
 | 
						|
            context->signalSemaphores = NULL;
 | 
						|
        }
 | 
						|
        if (context->commandBuffers) {
 | 
						|
            context->vkFreeCommandBuffers(context->device, context->commandPool, context->commandBufferCount, context->commandBuffers);
 | 
						|
            SDL_free(context->commandBuffers);
 | 
						|
            context->commandBuffers = NULL;
 | 
						|
        }
 | 
						|
        if (context->commandPool) {
 | 
						|
            context->vkDestroyCommandPool(context->device, context->commandPool, NULL);
 | 
						|
            context->commandPool = VK_NULL_HANDLE;
 | 
						|
        }
 | 
						|
        if (context->device) {
 | 
						|
            context->vkDestroyDevice(context->device, NULL);
 | 
						|
        }
 | 
						|
        if (context->surface) {
 | 
						|
            context->vkDestroySurfaceKHR(context->instance, context->surface, NULL);
 | 
						|
        }
 | 
						|
        if (context->instance) {
 | 
						|
            context->vkDestroyInstance(context->instance, NULL);
 | 
						|
        }
 | 
						|
        SDL_free(context);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#else
 | 
						|
 | 
						|
VulkanVideoContext *CreateVulkanVideoContext(SDL_Window *window)
 | 
						|
{
 | 
						|
    SDL_SetError("testffmpeg not built with Vulkan support");
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
void SetupVulkanRenderProperties(VulkanVideoContext *context, SDL_PropertiesID props)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceContext *ctx)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
SDL_Texture *CreateVulkanVideoTexture(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer, SDL_PropertiesID props)
 | 
						|
{
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
int BeginVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer)
 | 
						|
{
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
int FinishVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer)
 | 
						|
{
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
void DestroyVulkanVideoContext(VulkanVideoContext *context)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
#endif // FFMPEG_VULKAN_SUPPORT
 |