blob: 727ea5c846556abf63c0967532fcca66ebfa4981 [file] [log] [blame]
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// VulkanExternalHelper.cpp : Helper for allocating & managing vulkan external objects.
#include "test_utils/VulkanExternalHelper.h"
#include <vulkan/vulkan.h>
#include <vector>
#include "common/bitset_utils.h"
#include "common/debug.h"
namespace angle
{
namespace
{
constexpr VkImageUsageFlags kRequiredImageUsageFlags =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
std::vector<VkExtensionProperties> EnumerateInstanceExtensionProperties(const char *layerName)
{
uint32_t instanceExtensionCount;
VkResult result =
vkEnumerateInstanceExtensionProperties(layerName, &instanceExtensionCount, nullptr);
ASSERT(result == VK_SUCCESS);
std::vector<VkExtensionProperties> instanceExtensionProperties(instanceExtensionCount);
result = vkEnumerateInstanceExtensionProperties(layerName, &instanceExtensionCount,
instanceExtensionProperties.data());
ASSERT(result == VK_SUCCESS);
return instanceExtensionProperties;
}
std::vector<VkPhysicalDevice> EnumeratePhysicalDevices(VkInstance instance)
{
uint32_t physicalDeviceCount;
VkResult result = vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr);
ASSERT(result == VK_SUCCESS);
std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
result = vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices.data());
return physicalDevices;
}
std::vector<VkExtensionProperties> EnumerateDeviceExtensionProperties(
VkPhysicalDevice physicalDevice,
const char *layerName)
{
uint32_t deviceExtensionCount;
VkResult result = vkEnumerateDeviceExtensionProperties(physicalDevice, layerName,
&deviceExtensionCount, nullptr);
ASSERT(result == VK_SUCCESS);
std::vector<VkExtensionProperties> deviceExtensionProperties(deviceExtensionCount);
result = vkEnumerateDeviceExtensionProperties(physicalDevice, layerName, &deviceExtensionCount,
deviceExtensionProperties.data());
ASSERT(result == VK_SUCCESS);
return deviceExtensionProperties;
}
std::vector<VkQueueFamilyProperties> GetPhysicalDeviceQueueFamilyProperties(
VkPhysicalDevice physicalDevice)
{
uint32_t queueFamilyPropertyCount;
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyPropertyCount, nullptr);
std::vector<VkQueueFamilyProperties> physicalDeviceQueueFamilyProperties(
queueFamilyPropertyCount);
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyPropertyCount,
physicalDeviceQueueFamilyProperties.data());
return physicalDeviceQueueFamilyProperties;
}
bool HasExtension(const std::vector<VkExtensionProperties> instanceExtensions,
const char *extensionName)
{
for (const auto &extensionProperties : instanceExtensions)
{
if (!strcmp(extensionProperties.extensionName, extensionName))
return true;
}
return false;
}
bool HasExtension(const std::vector<const char *> enabledExtensions, const char *extensionName)
{
for (const char *enabledExtension : enabledExtensions)
{
if (!strcmp(enabledExtension, extensionName))
return true;
}
return false;
}
uint32_t FindMemoryType(const VkPhysicalDeviceMemoryProperties &memoryProperties,
uint32_t memoryTypeBits,
VkMemoryPropertyFlags requiredMemoryPropertyFlags)
{
for (size_t memoryIndex : angle::BitSet32<32>(memoryTypeBits))
{
ASSERT(memoryIndex < memoryProperties.memoryTypeCount);
if ((memoryProperties.memoryTypes[memoryIndex].propertyFlags &
requiredMemoryPropertyFlags) == requiredMemoryPropertyFlags)
{
return static_cast<uint32_t>(memoryIndex);
}
}
return UINT32_MAX;
}
} // namespace
VulkanExternalHelper::VulkanExternalHelper() {}
VulkanExternalHelper::~VulkanExternalHelper()
{
if (mDevice != VK_NULL_HANDLE)
{
vkDeviceWaitIdle(mDevice);
vkDestroyDevice(mDevice, nullptr);
mDevice = VK_NULL_HANDLE;
mGraphicsQueue = VK_NULL_HANDLE;
}
if (mInstance != VK_NULL_HANDLE)
{
vkDestroyInstance(mInstance, nullptr);
mInstance = VK_NULL_HANDLE;
}
}
void VulkanExternalHelper::initialize()
{
ASSERT(mInstance == VK_NULL_HANDLE);
std::vector<VkExtensionProperties> instanceExtensionProperties =
EnumerateInstanceExtensionProperties(nullptr);
std::vector<const char *> requestedInstanceExtensions = {
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,
VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME};
std::vector<const char *> enabledInstanceExtensions;
for (const char *extensionName : requestedInstanceExtensions)
{
if (HasExtension(instanceExtensionProperties, extensionName))
{
enabledInstanceExtensions.push_back(extensionName);
}
}
VkApplicationInfo applicationInfo = {
/* .sType = */ VK_STRUCTURE_TYPE_APPLICATION_INFO,
/* .pNext = */ nullptr,
/* .pApplicationName = */ "ANGLE Tests",
/* .applicationVersion = */ 1,
/* .pEngineName = */ nullptr,
/* .engineVersion = */ 0,
/* .apiVersion = */ VK_API_VERSION_1_0,
};
uint32_t enabledInstanceExtensionCount =
static_cast<uint32_t>(enabledInstanceExtensions.size());
VkInstanceCreateInfo instanceCreateInfo = {
/* .sType = */ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
/* .pNext = */ nullptr,
/* .flags = */ 0,
/* .pApplicationInfo = */ &applicationInfo,
/* .enabledLayerCount = */ 0,
/* .ppEnabledLayerNames = */ nullptr,
/* .enabledExtensionCount = */ enabledInstanceExtensionCount,
/* .ppEnabledExtensionName = */ enabledInstanceExtensions.data(),
};
VkResult result = vkCreateInstance(&instanceCreateInfo, nullptr, &mInstance);
ASSERT(result == VK_SUCCESS);
ASSERT(mInstance != VK_NULL_HANDLE);
std::vector<VkPhysicalDevice> physicalDevices = EnumeratePhysicalDevices(mInstance);
ASSERT(physicalDevices.size() > 0);
mPhysicalDevice = physicalDevices[0];
vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &mMemoryProperties);
std::vector<VkExtensionProperties> deviceExtensionProperties =
EnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr);
std::vector<const char *> requestedDeviceExtensions = {
VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME,
VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
};
std::vector<const char *> enabledDeviceExtensions;
for (const char *extensionName : requestedDeviceExtensions)
{
if (HasExtension(deviceExtensionProperties, extensionName))
{
enabledDeviceExtensions.push_back(extensionName);
}
}
std::vector<VkQueueFamilyProperties> queueFamilyProperties =
GetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice);
for (uint32_t i = 0; i < queueFamilyProperties.size(); ++i)
{
if (queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
{
mGraphicsQueueFamilyIndex = i;
}
}
ASSERT(mGraphicsQueueFamilyIndex != UINT32_MAX);
constexpr uint32_t kQueueCreateInfoCount = 1;
constexpr uint32_t kGraphicsQueueCount = 1;
float graphicsQueuePriorities[kGraphicsQueueCount] = {0.f};
VkDeviceQueueCreateInfo queueCreateInfos[kQueueCreateInfoCount] = {
/* [0] = */ {
/* .sType = */ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
/* .pNext = */ nullptr,
/* .flags = */ 0,
/* .queueFamilyIndex = */ mGraphicsQueueFamilyIndex,
/* .queueCount = */ 1,
/* .pQueuePriorities = */ graphicsQueuePriorities,
},
};
uint32_t enabledDeviceExtensionCount = static_cast<uint32_t>(enabledDeviceExtensions.size());
VkDeviceCreateInfo deviceCreateInfo = {
/* .sType = */ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
/* .pNext = */ nullptr,
/* .flags = */ 0,
/* .queueCreateInfoCount = */ kQueueCreateInfoCount,
/* .pQueueCreateInfos = */ queueCreateInfos,
/* .enabledLayerCount = */ 0,
/* .ppEnabledLayerNames = */ nullptr,
/* .enabledExtensionCount = */ enabledDeviceExtensionCount,
/* .ppEnabledExtensionName = */ enabledDeviceExtensions.data(),
/* .pEnabledFeatures = */ nullptr,
};
result = vkCreateDevice(mPhysicalDevice, &deviceCreateInfo, nullptr, &mDevice);
ASSERT(result == VK_SUCCESS);
ASSERT(mDevice != VK_NULL_HANDLE);
constexpr uint32_t kGraphicsQueueIndex = 0;
static_assert(kGraphicsQueueIndex < kGraphicsQueueCount, "must be in range");
vkGetDeviceQueue(mDevice, mGraphicsQueueFamilyIndex, kGraphicsQueueIndex, &mGraphicsQueue);
ASSERT(mGraphicsQueue != VK_NULL_HANDLE);
mHasExternalMemoryFd =
HasExtension(enabledDeviceExtensions, VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
mHasExternalSemaphoreFd =
HasExtension(enabledDeviceExtensions, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME);
vkGetPhysicalDeviceImageFormatProperties2 =
reinterpret_cast<PFN_vkGetPhysicalDeviceImageFormatProperties2>(
vkGetInstanceProcAddr(mInstance, "vkGetPhysicalDeviceImageFormatProperties2"));
vkGetMemoryFdKHR = reinterpret_cast<PFN_vkGetMemoryFdKHR>(
vkGetInstanceProcAddr(mInstance, "vkGetMemoryFdKHR"));
vkGetSemaphoreFdKHR = reinterpret_cast<PFN_vkGetSemaphoreFdKHR>(
vkGetInstanceProcAddr(mInstance, "vkGetSemaphoreFdKHR"));
vkGetPhysicalDeviceExternalSemaphorePropertiesKHR =
reinterpret_cast<PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR>(
vkGetInstanceProcAddr(mInstance, "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR"));
}
bool VulkanExternalHelper::canCreateImageOpaqueFd(VkFormat format,
VkImageType type,
VkImageTiling tiling) const
{
if (!mHasExternalMemoryFd)
{
return false;
}
VkPhysicalDeviceExternalImageFormatInfo externalImageFormatInfo = {
/* .sType = */ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
/* .pNext = */ nullptr,
/* .handleType = */ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
};
VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = {
/* .sType = */ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
/* .pNext = */ &externalImageFormatInfo,
/* .format = */ format,
/* .type = */ type,
/* .tiling = */ tiling,
/* .usage = */ kRequiredImageUsageFlags,
/* .flags = */ 0,
};
VkExternalImageFormatProperties externalImageFormatProperties = {
/* .sType = */ VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES,
/* .pNext = */ nullptr,
};
VkImageFormatProperties2 imageFormatProperties = {
/* .sType = */ VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2,
/* .pNext = */ &externalImageFormatProperties};
VkResult result = vkGetPhysicalDeviceImageFormatProperties2(mPhysicalDevice, &imageFormatInfo,
&imageFormatProperties);
if (result == VK_ERROR_FORMAT_NOT_SUPPORTED)
{
return false;
}
ASSERT(result == VK_SUCCESS);
constexpr VkExternalMemoryFeatureFlags kUnsupportedFeatures =
VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT;
constexpr VkExternalMemoryFeatureFlags kRequiredFeatures =
VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT;
if (externalImageFormatProperties.externalMemoryProperties.externalMemoryFeatures &
kUnsupportedFeatures)
{
return false;
}
if ((externalImageFormatProperties.externalMemoryProperties.externalMemoryFeatures &
kRequiredFeatures) != kRequiredFeatures)
{
return false;
}
return true;
}
VkResult VulkanExternalHelper::createImage2DOpaqueFd(VkFormat format,
VkExtent3D extent,
VkImage *imageOut,
VkDeviceMemory *deviceMemoryOut,
VkDeviceSize *deviceMemorySizeOut)
{
VkExternalMemoryImageCreateInfoKHR externalMemoryImageCreateInfo = {
/* .sType = */ VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
/* .pNext = */ nullptr,
/* .handleTypes = */ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
};
VkImageCreateInfo imageCreateInfo = {
/* .sType = */ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
/* .pNext = */ &externalMemoryImageCreateInfo,
/* .flags = */ 0,
/* .imageType = */ VK_IMAGE_TYPE_2D,
/* .format = */ format,
/* .extent = */ extent,
/* .mipLevels = */ 1,
/* .arrayLayers = */ 1,
/* .samples = */ VK_SAMPLE_COUNT_1_BIT,
/* .tiling = */ VK_IMAGE_TILING_OPTIMAL,
/* .usage = */ kRequiredImageUsageFlags,
/* .sharingMode = */ VK_SHARING_MODE_EXCLUSIVE,
/* .queueFamilyIndexCount = */ 0,
/* .pQueueFamilyIndices = */ nullptr,
/* initialLayout = */ VK_IMAGE_LAYOUT_UNDEFINED,
};
VkImage image = VK_NULL_HANDLE;
VkResult result = vkCreateImage(mDevice, &imageCreateInfo, nullptr, &image);
if (result != VK_SUCCESS)
{
return result;
}
VkMemoryPropertyFlags requestedMemoryPropertyFlags = 0;
VkMemoryRequirements memoryRequirements;
vkGetImageMemoryRequirements(mDevice, image, &memoryRequirements);
uint32_t memoryTypeIndex = FindMemoryType(mMemoryProperties, memoryRequirements.memoryTypeBits,
requestedMemoryPropertyFlags);
ASSERT(memoryTypeIndex != UINT32_MAX);
VkDeviceSize deviceMemorySize = memoryRequirements.size;
VkExportMemoryAllocateInfo exportMemoryAllocateInfo = {
/* .sType = */ VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO,
/* .pNext = */ nullptr,
/* .handleTypes = */ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
};
VkMemoryAllocateInfo memoryAllocateInfo = {
/* .sType = */ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
/* .pNext = */ &exportMemoryAllocateInfo,
/* .allocationSize = */ deviceMemorySize,
/* .memoryTypeIndex = */ memoryTypeIndex,
};
VkDeviceMemory deviceMemory = VK_NULL_HANDLE;
result = vkAllocateMemory(mDevice, &memoryAllocateInfo, nullptr, &deviceMemory);
if (result != VK_SUCCESS)
{
vkDestroyImage(mDevice, image, nullptr);
return result;
}
VkDeviceSize memoryOffset = 0;
result = vkBindImageMemory(mDevice, image, deviceMemory, memoryOffset);
if (result != VK_SUCCESS)
{
vkFreeMemory(mDevice, deviceMemory, nullptr);
vkDestroyImage(mDevice, image, nullptr);
return result;
}
*imageOut = image;
*deviceMemoryOut = deviceMemory;
*deviceMemorySizeOut = deviceMemorySize;
return VK_SUCCESS;
}
VkResult VulkanExternalHelper::exportMemoryOpaqueFd(VkDeviceMemory deviceMemory, int *fd)
{
VkMemoryGetFdInfoKHR memoryGetFdInfo = {
/* .sType = */ VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
/* .pNext = */ nullptr,
/* .memory = */ deviceMemory,
/* .handleType = */ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
};
return vkGetMemoryFdKHR(mDevice, &memoryGetFdInfo, fd);
}
bool VulkanExternalHelper::canCreateSemaphoreOpaqueFd() const
{
if (!mHasExternalSemaphoreFd || !vkGetPhysicalDeviceExternalSemaphorePropertiesKHR)
{
return false;
}
VkPhysicalDeviceExternalSemaphoreInfo externalSemaphoreInfo = {
/* .sType = */ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO,
/* .pNext = */ nullptr,
/* .handleType = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
};
VkExternalSemaphoreProperties externalSemaphoreProperties = {};
vkGetPhysicalDeviceExternalSemaphorePropertiesKHR(mPhysicalDevice, &externalSemaphoreInfo,
&externalSemaphoreProperties);
constexpr VkExternalSemaphoreFeatureFlags kRequiredFeatures =
VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT;
if ((externalSemaphoreProperties.externalSemaphoreFeatures & kRequiredFeatures) !=
kRequiredFeatures)
{
return false;
}
return true;
}
VkResult VulkanExternalHelper::createSemaphoreOpaqueFd(VkSemaphore *semaphore)
{
VkExportSemaphoreCreateInfo exportSemaphoreCreateInfo = {
/* .sType = */ VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO,
/* .pNext = */ nullptr,
/* .handleTypes = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
};
VkSemaphoreCreateInfo semaphoreCreateInfo = {
/* .sType = */ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
/* .pNext = */ &exportSemaphoreCreateInfo,
/* .flags = */ 0,
};
return vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, semaphore);
}
VkResult VulkanExternalHelper::exportSemaphoreOpaqueFd(VkSemaphore semaphore, int *fd)
{
VkSemaphoreGetFdInfoKHR semaphoreGetFdInfo = {
/* .sType = */ VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
/* .pNext = */ nullptr,
/* .semaphore = */ semaphore,
/* .handleType = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
};
return vkGetSemaphoreFdKHR(mDevice, &semaphoreGetFdInfo, fd);
}
} // namespace angle