pipewire/spa/plugins/vulkan/vulkan-utils.c

327 lines
8.5 KiB
C
Raw Normal View History

/* Spa */
/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */
/* SPDX-License-Identifier: MIT */
#include <vulkan/vulkan.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
#if !defined(__FreeBSD__) && !defined(__MidnightBSD__)
#include <alloca.h>
2020-03-27 19:20:21 +00:00
#endif
#include <errno.h>
#include <stdio.h>
#include <assert.h>
#include <math.h>
#include <time.h>
#include <spa/utils/result.h>
#include <spa/utils/string.h>
#include <spa/param/video/format.h>
#include <spa/support/log.h>
#include <spa/debug/mem.h>
#include "vulkan-utils.h"
2022-06-01 10:54:15 +02:00
//#define ENABLE_VALIDATION
static int vkresult_to_errno(VkResult result)
{
switch (result) {
case VK_SUCCESS:
case VK_EVENT_SET:
case VK_EVENT_RESET:
return 0;
case VK_NOT_READY:
case VK_INCOMPLETE:
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
return EBUSY;
case VK_TIMEOUT:
return ETIMEDOUT;
case VK_ERROR_OUT_OF_HOST_MEMORY:
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
case VK_ERROR_MEMORY_MAP_FAILED:
case VK_ERROR_OUT_OF_POOL_MEMORY:
case VK_ERROR_FRAGMENTED_POOL:
#ifdef VK_ERROR_FRAGMENTATION_EXT
case VK_ERROR_FRAGMENTATION_EXT:
#endif
return ENOMEM;
case VK_ERROR_INITIALIZATION_FAILED:
return EIO;
case VK_ERROR_DEVICE_LOST:
case VK_ERROR_SURFACE_LOST_KHR:
#ifdef VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT
case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
#endif
return ENODEV;
case VK_ERROR_LAYER_NOT_PRESENT:
case VK_ERROR_EXTENSION_NOT_PRESENT:
case VK_ERROR_FEATURE_NOT_PRESENT:
return ENOENT;
case VK_ERROR_INCOMPATIBLE_DRIVER:
case VK_ERROR_FORMAT_NOT_SUPPORTED:
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
return ENOTSUP;
case VK_ERROR_TOO_MANY_OBJECTS:
return ENFILE;
case VK_SUBOPTIMAL_KHR:
case VK_ERROR_OUT_OF_DATE_KHR:
return EIO;
case VK_ERROR_INVALID_EXTERNAL_HANDLE:
case VK_ERROR_INVALID_SHADER_NV:
#ifdef VK_ERROR_VALIDATION_FAILED_EXT
case VK_ERROR_VALIDATION_FAILED_EXT:
#endif
#ifdef VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT
case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:
#endif
#ifdef VK_ERROR_INVALID_DEVICE_ADDRESS_EXT
case VK_ERROR_INVALID_DEVICE_ADDRESS_EXT:
#endif
return EINVAL;
#ifdef VK_ERROR_NOT_PERMITTED_EXT
case VK_ERROR_NOT_PERMITTED_EXT:
return EPERM;
#endif
default:
return EIO;
}
}
static struct {
VkFormat format;
uint32_t id;
} vk_video_format_convs[] = {
{ VK_FORMAT_R32G32B32A32_SFLOAT, SPA_VIDEO_FORMAT_RGBA_F32 },
};
static int createInstance(struct vulkan_base *s)
{
static const VkApplicationInfo applicationInfo = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
2019-08-19 16:47:02 +02:00
.pApplicationName = "PipeWire",
.applicationVersion = 0,
2019-08-19 16:47:02 +02:00
.pEngineName = "PipeWire Vulkan Engine",
.engineVersion = 0,
.apiVersion = VK_API_VERSION_1_1
};
static const char * const extensions[] = {
VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME
};
static const char * const checkLayers[] = {
#ifdef ENABLE_VALIDATION
2022-05-31 09:53:08 +02:00
"VK_LAYER_KHRONOS_validation",
#endif
NULL
2022-05-31 09:53:08 +02:00
};
uint32_t i, j, layerCount, n_layers = 0;
const char *layers[1];
2022-05-31 09:53:08 +02:00
vkEnumerateInstanceLayerProperties(&layerCount, NULL);
VkLayerProperties availableLayers[layerCount];
vkEnumerateInstanceLayerProperties(&layerCount, availableLayers);
for (i = 0; i < layerCount; i++) {
for (j = 0; j < SPA_N_ELEMENTS(checkLayers); j++) {
if (spa_streq(availableLayers[i].layerName, checkLayers[j]))
layers[n_layers++] = checkLayers[j];
}
}
const VkInstanceCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &applicationInfo,
.enabledExtensionCount = 1,
.ppEnabledExtensionNames = extensions,
.enabledLayerCount = n_layers,
2022-05-31 09:53:08 +02:00
.ppEnabledLayerNames = layers,
};
VK_CHECK_RESULT(vkCreateInstance(&createInfo, NULL, &s->instance));
return 0;
}
static int findPhysicalDevice(struct vulkan_base *s)
{
uint32_t deviceCount;
VkPhysicalDevice *devices;
vkEnumeratePhysicalDevices(s->instance, &deviceCount, NULL);
if (deviceCount == 0)
return -ENODEV;
devices = alloca(deviceCount * sizeof(VkPhysicalDevice));
vkEnumeratePhysicalDevices(s->instance, &deviceCount, devices);
s->physicalDevice = devices[0];
return 0;
}
static int getComputeQueueFamilyIndex(struct vulkan_base *s, uint32_t queueFlags, uint32_t *queueFamilyIndex)
{
uint32_t i, queueFamilyCount;
VkQueueFamilyProperties *queueFamilies;
vkGetPhysicalDeviceQueueFamilyProperties(s->physicalDevice, &queueFamilyCount, NULL);
queueFamilies = alloca(queueFamilyCount * sizeof(VkQueueFamilyProperties));
vkGetPhysicalDeviceQueueFamilyProperties(s->physicalDevice, &queueFamilyCount, queueFamilies);
for (i = 0; i < queueFamilyCount; i++) {
VkQueueFamilyProperties props = queueFamilies[i];
if (props.queueCount > 0 && ((props.queueFlags & queueFlags) == queueFlags))
break;
}
if (i == queueFamilyCount)
return -ENODEV;
*queueFamilyIndex = i;
return 0;
}
static int createDevice(struct vulkan_base *s, struct vulkan_base_info *info)
{
CHECK(getComputeQueueFamilyIndex(s, info->queueFlags, &s->queueFamilyIndex));
const VkDeviceQueueCreateInfo queueCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = s->queueFamilyIndex,
.queueCount = 1,
.pQueuePriorities = (const float[]) { 1.0f }
};
static const char * const extensions[] = {
VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME
};
const VkDeviceCreateInfo deviceCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &queueCreateInfo,
.enabledExtensionCount = 2,
.ppEnabledExtensionNames = extensions,
};
VK_CHECK_RESULT(vkCreateDevice(s->physicalDevice, &deviceCreateInfo, NULL, &s->device));
vkGetDeviceQueue(s->device, s->queueFamilyIndex, 0, &s->queue);
return 0;
}
int vulkan_commandPool_create(struct vulkan_base *s, VkCommandPool *commandPool)
{
const VkCommandPoolCreateInfo commandPoolCreateInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = s->queueFamilyIndex,
};
VK_CHECK_RESULT(vkCreateCommandPool(s->device,
&commandPoolCreateInfo, NULL,
commandPool));
return 0;
}
int vulkan_commandBuffer_create(struct vulkan_base *s, VkCommandPool commandPool, VkCommandBuffer *commandBuffer)
{
const VkCommandBufferAllocateInfo commandBufferAllocateInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = commandPool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
VK_CHECK_RESULT(vkAllocateCommandBuffers(s->device,
&commandBufferAllocateInfo,
commandBuffer));
return 0;
}
uint32_t vulkan_memoryType_find(struct vulkan_base *s,
uint32_t memoryTypeBits, VkMemoryPropertyFlags properties)
{
uint32_t i;
VkPhysicalDeviceMemoryProperties memoryProperties;
vkGetPhysicalDeviceMemoryProperties(s->physicalDevice, &memoryProperties);
for (i = 0; i < memoryProperties.memoryTypeCount; i++) {
if ((memoryTypeBits & (1 << i)) &&
((memoryProperties.memoryTypes[i].propertyFlags & properties) == properties))
return i;
}
return -1;
}
void vulkan_buffer_clear(struct vulkan_base *s, struct vulkan_buffer *buffer)
{
if (buffer->fd != -1)
close(buffer->fd);
vkFreeMemory(s->device, buffer->memory, NULL);
vkDestroyImage(s->device, buffer->image, NULL);
vkDestroyImageView(s->device, buffer->view, NULL);
}
int vulkan_stream_init(struct vulkan_stream *stream, enum spa_direction direction,
struct spa_dict *props)
{
spa_zero(*stream);
stream->direction = direction;
stream->current_buffer_id = SPA_ID_INVALID;
stream->busy_buffer_id = SPA_ID_INVALID;
stream->ready_buffer_id = SPA_ID_INVALID;
return 0;
}
uint32_t vulkan_vkformat_to_id(VkFormat format)
{
SPA_FOR_EACH_ELEMENT_VAR(vk_video_format_convs, f) {
if (f->format == format)
return f->id;
}
return SPA_VIDEO_FORMAT_UNKNOWN;
}
VkFormat vulkan_id_to_vkformat(uint32_t id)
{
SPA_FOR_EACH_ELEMENT_VAR(vk_video_format_convs, f) {
if (f->id == id)
return f->format;
}
return VK_FORMAT_UNDEFINED;
}
int vulkan_vkresult_to_errno(VkResult result)
{
return vkresult_to_errno(result);
}
int vulkan_base_init(struct vulkan_base *s, struct vulkan_base_info *info)
{
if (!s->initialized) {
2022-05-31 11:33:51 +02:00
CHECK(createInstance(s));
CHECK(findPhysicalDevice(s));
CHECK(createDevice(s, info));
s->initialized = true;
}
return 0;
}
void vulkan_base_deinit(struct vulkan_base *s)
{
if (s->initialized) {
vkDestroyDevice(s->device, NULL);
vkDestroyInstance(s->instance, NULL);
s->initialized = false;
}
}