blob: 5b4f35084fcbcb47527468d5dbc0f3523be0e65e [file] [log] [blame]
/**************************************************************************
*
* Copyright (C) 2022 Collabora Ltd
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
**************************************************************************/
#include "vkr_allocator.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "util/list.h"
#include "venus-protocol/vulkan.h"
#include "virgl_resource.h"
/* Assume that we will deal with at most 4 devices.
* This is to avoid per-device resource dynamic allocations.
* For now, `vkr_allocator` is designed for Mesa CI use which
* uses lavapipe as the only Vulkan driver, but allow logic to
* assume more for some leeway and felxibilty; especially if
* this allocator is expanded to use whatever devices available.
*/
#define VKR_ALLOCATOR_MAX_DEVICE_COUNT 4
struct vkr_opaque_fd_mem_info {
VkDevice device;
VkDeviceMemory device_memory;
uint32_t res_id;
uint64_t size;
struct list_head head;
};
static struct vkr_allocator {
VkInstance instance;
VkPhysicalDevice physical_devices[VKR_ALLOCATOR_MAX_DEVICE_COUNT];
VkDevice devices[VKR_ALLOCATOR_MAX_DEVICE_COUNT];
uint8_t device_uuids[VKR_ALLOCATOR_MAX_DEVICE_COUNT][VK_UUID_SIZE];
uint32_t device_count;
struct list_head memories;
} vkr_allocator;
static bool vkr_allocator_initialized;
static void
vkr_allocator_free_memory(struct vkr_opaque_fd_mem_info *mem_info)
{
vkFreeMemory(mem_info->device, mem_info->device_memory, NULL);
list_del(&mem_info->head);
free(mem_info);
}
static VkDevice
vkr_allocator_get_device(struct virgl_resource *res)
{
for (uint32_t i = 0; i < vkr_allocator.device_count; ++i) {
if (memcmp(vkr_allocator.device_uuids[i], res->vulkan_info.device_uuid,
VK_UUID_SIZE) == 0)
return vkr_allocator.devices[i];
}
return VK_NULL_HANDLE;
}
static struct vkr_opaque_fd_mem_info *
vkr_allocator_allocate_memory(struct virgl_resource *res)
{
VkDevice dev_handle = vkr_allocator_get_device(res);
if (dev_handle == VK_NULL_HANDLE)
return NULL;
int fd = -1;
if (virgl_resource_export_fd(res, &fd) != VIRGL_RESOURCE_FD_OPAQUE) {
if (fd >= 0)
close(fd);
return NULL;
}
VkMemoryAllocateInfo alloc_info = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext =
&(VkImportMemoryFdInfoKHR){ .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR,
.handleType =
VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
.fd = fd },
.allocationSize = res->vulkan_info.allocation_size,
.memoryTypeIndex = res->vulkan_info.memory_type_index
};
VkDeviceMemory mem_handle;
if (vkAllocateMemory(dev_handle, &alloc_info, NULL, &mem_handle) != VK_SUCCESS) {
close(fd);
return NULL;
}
struct vkr_opaque_fd_mem_info *mem_info = calloc(1, sizeof(*mem_info));
if (!mem_info) {
vkFreeMemory(dev_handle, mem_handle, NULL);
return NULL;
}
mem_info->device = dev_handle;
mem_info->device_memory = mem_handle;
mem_info->res_id = res->res_id;
mem_info->size = res->vulkan_info.allocation_size;
list_addtail(&mem_info->head, &vkr_allocator.memories);
return mem_info;
}
void
vkr_allocator_fini(void)
{
if (!vkr_allocator_initialized)
return;
struct vkr_opaque_fd_mem_info *mem_info, *mem_info_temp;
LIST_FOR_EACH_ENTRY_SAFE (mem_info, mem_info_temp, &vkr_allocator.memories, head)
vkr_allocator_free_memory(mem_info);
for (uint32_t i = 0; i < vkr_allocator.device_count; ++i) {
vkDestroyDevice(vkr_allocator.devices[i], NULL);
}
vkDestroyInstance(vkr_allocator.instance, NULL);
memset(&vkr_allocator, 0, sizeof(vkr_allocator));
vkr_allocator_initialized = false;
}
int
vkr_allocator_init(void)
{
VkResult res;
VkApplicationInfo app_info = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.apiVersion = VK_API_VERSION_1_1,
};
VkInstanceCreateInfo inst_info = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &app_info,
};
res = vkCreateInstance(&inst_info, NULL, &vkr_allocator.instance);
if (res != VK_SUCCESS)
goto fail;
vkr_allocator.device_count = VKR_ALLOCATOR_MAX_DEVICE_COUNT;
res = vkEnumeratePhysicalDevices(vkr_allocator.instance, &vkr_allocator.device_count,
vkr_allocator.physical_devices);
if (res != VK_SUCCESS && res != VK_INCOMPLETE)
goto fail;
for (uint32_t i = 0; i < vkr_allocator.device_count; ++i) {
VkPhysicalDevice physical_dev_handle = vkr_allocator.physical_devices[i];
VkPhysicalDeviceIDProperties id_props = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES
};
VkPhysicalDeviceProperties2 props2 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = &id_props
};
vkGetPhysicalDeviceProperties2(physical_dev_handle, &props2);
memcpy(vkr_allocator.device_uuids[i], id_props.deviceUUID, VK_UUID_SIZE);
float priority = 1.0;
VkDeviceQueueCreateInfo queue_info = {
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
/* Use any queue since we dont really need it.
* We are guaranteed at least one by the spec */
.queueFamilyIndex = 0,
.queueCount = 1,
.pQueuePriorities = &priority
};
VkDeviceCreateInfo dev_info = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &queue_info,
};
res =
vkCreateDevice(physical_dev_handle, &dev_info, NULL, &vkr_allocator.devices[i]);
if (res != VK_SUCCESS)
goto fail;
}
list_inithead(&vkr_allocator.memories);
return 0;
fail:
for (uint32_t i = 0; i < vkr_allocator.device_count; ++i) {
vkDestroyDevice(vkr_allocator.devices[i], NULL);
}
vkDestroyInstance(vkr_allocator.instance, NULL);
memset(&vkr_allocator, 0, sizeof(vkr_allocator));
return -1;
}
int
vkr_allocator_resource_map(struct virgl_resource *res, void **map, uint64_t *out_size)
{
if (!vkr_allocator_initialized) {
if (vkr_allocator_init())
return -EINVAL;
vkr_allocator_initialized = true;
}
assert(vkr_allocator_initialized);
struct vkr_opaque_fd_mem_info *mem_info = vkr_allocator_allocate_memory(res);
if (!mem_info)
return -EINVAL;
void *ptr;
if (vkMapMemory(mem_info->device, mem_info->device_memory, 0, mem_info->size, 0,
&ptr) != VK_SUCCESS) {
vkr_allocator_free_memory(mem_info);
return -EINVAL;
}
*map = ptr;
*out_size = mem_info->size;
return 0;
}
static struct vkr_opaque_fd_mem_info *
vkr_allocator_get_mem_info(struct virgl_resource *res)
{
struct vkr_opaque_fd_mem_info *mem_info, *mem_info_temp;
LIST_FOR_EACH_ENTRY_SAFE (mem_info, mem_info_temp, &vkr_allocator.memories, head)
if (mem_info->res_id == res->res_id)
return mem_info;
return NULL;
}
int
vkr_allocator_resource_unmap(struct virgl_resource *res)
{
assert(vkr_allocator_initialized);
struct vkr_opaque_fd_mem_info *mem_info = vkr_allocator_get_mem_info(res);
if (!mem_info)
return -EINVAL;
vkUnmapMemory(mem_info->device, mem_info->device_memory);
vkr_allocator_free_memory(mem_info);
return 0;
}