mesa/src/virtio/vulkan/vn_device_memory.c

641 lines
22 KiB
C

/*
* Copyright 2019 Google LLC
* SPDX-License-Identifier: MIT
*
* based in part on anv and radv which are:
* Copyright © 2015 Intel Corporation
* Copyright © 2016 Red Hat.
* Copyright © 2016 Bas Nieuwenhuizen
*/
#include "vn_device_memory.h"
#include "venus-protocol/vn_protocol_driver_device_memory.h"
#include "venus-protocol/vn_protocol_driver_transport.h"
#include "vn_android.h"
#include "vn_buffer.h"
#include "vn_device.h"
#include "vn_image.h"
#include "vn_physical_device.h"
#include "vn_renderer.h"
#include "vn_renderer_util.h"
/* device memory commands */
static inline VkResult
vn_device_memory_alloc_simple(struct vn_device *dev,
struct vn_device_memory *mem,
const VkMemoryAllocateInfo *alloc_info)
{
VkDevice dev_handle = vn_device_to_handle(dev);
VkDeviceMemory mem_handle = vn_device_memory_to_handle(mem);
if (VN_PERF(NO_ASYNC_MEM_ALLOC)) {
return vn_call_vkAllocateMemory(dev->primary_ring, dev_handle,
alloc_info, NULL, &mem_handle);
}
struct vn_ring_submit_command ring_submit;
vn_submit_vkAllocateMemory(dev->primary_ring, 0, dev_handle, alloc_info,
NULL, &mem_handle, &ring_submit);
if (!ring_submit.ring_seqno_valid)
return VK_ERROR_OUT_OF_HOST_MEMORY;
mem->bo_ring_seqno_valid = true;
mem->bo_ring_seqno = ring_submit.ring_seqno;
return VK_SUCCESS;
}
static inline void
vn_device_memory_free_simple(struct vn_device *dev,
struct vn_device_memory *mem)
{
VkDevice dev_handle = vn_device_to_handle(dev);
VkDeviceMemory mem_handle = vn_device_memory_to_handle(mem);
vn_async_vkFreeMemory(dev->primary_ring, dev_handle, mem_handle, NULL);
}
static VkResult
vn_device_memory_wait_alloc(struct vn_device *dev,
struct vn_device_memory *mem)
{
if (!mem->bo_ring_seqno_valid)
return VK_SUCCESS;
/* no need to wait for ring if
* - mem alloc is done upon bo map or export
* - mem import is done upon bo destroy
*/
if (vn_ring_get_seqno_status(dev->primary_ring, mem->bo_ring_seqno))
return VK_SUCCESS;
/* fine to false it here since renderer submission failure is fatal */
mem->bo_ring_seqno_valid = false;
const uint64_t ring_id = vn_ring_get_id(dev->primary_ring);
uint32_t local_data[8];
struct vn_cs_encoder local_enc =
VN_CS_ENCODER_INITIALIZER_LOCAL(local_data, sizeof(local_data));
vn_encode_vkWaitRingSeqnoMESA(&local_enc, 0, ring_id, mem->bo_ring_seqno);
return vn_renderer_submit_simple(dev->renderer, local_data,
vn_cs_encoder_get_len(&local_enc));
}
static inline VkResult
vn_device_memory_bo_init(struct vn_device *dev, struct vn_device_memory *mem)
{
VkResult result = vn_device_memory_wait_alloc(dev, mem);
if (result != VK_SUCCESS)
return result;
const struct vk_device_memory *mem_vk = &mem->base.base;
const VkMemoryType *mem_type = &dev->physical_device->memory_properties
.memoryTypes[mem_vk->memory_type_index];
return vn_renderer_bo_create_from_device_memory(
dev->renderer, mem_vk->size, mem->base.id, mem_type->propertyFlags,
mem_vk->export_handle_types, &mem->base_bo);
}
static inline void
vn_device_memory_bo_fini(struct vn_device *dev, struct vn_device_memory *mem)
{
if (mem->base_bo) {
vn_device_memory_wait_alloc(dev, mem);
vn_renderer_bo_unref(dev->renderer, mem->base_bo);
}
}
VkResult
vn_device_memory_import_dma_buf(struct vn_device *dev,
struct vn_device_memory *mem,
const VkMemoryAllocateInfo *alloc_info,
bool force_unmappable,
int fd)
{
const VkMemoryType *mem_type =
&dev->physical_device->memory_properties
.memoryTypes[alloc_info->memoryTypeIndex];
struct vn_renderer_bo *bo;
VkResult result = vn_renderer_bo_create_from_dma_buf(
dev->renderer, alloc_info->allocationSize, fd,
force_unmappable ? 0 : mem_type->propertyFlags, &bo);
if (result != VK_SUCCESS)
return result;
vn_ring_roundtrip(dev->primary_ring);
const VkImportMemoryResourceInfoMESA import_memory_resource_info = {
.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_RESOURCE_INFO_MESA,
.pNext = alloc_info->pNext,
.resourceId = bo->res_id,
};
const VkMemoryAllocateInfo memory_allocate_info = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = &import_memory_resource_info,
.allocationSize = alloc_info->allocationSize,
.memoryTypeIndex = alloc_info->memoryTypeIndex,
};
result = vn_device_memory_alloc_simple(dev, mem, &memory_allocate_info);
if (result != VK_SUCCESS) {
vn_renderer_bo_unref(dev->renderer, bo);
return result;
}
/* need to close import fd on success to avoid fd leak */
close(fd);
mem->base_bo = bo;
return VK_SUCCESS;
}
static VkResult
vn_device_memory_alloc_guest_vram(struct vn_device *dev,
struct vn_device_memory *mem,
const VkMemoryAllocateInfo *alloc_info)
{
const struct vk_device_memory *mem_vk = &mem->base.base;
const VkMemoryType *mem_type = &dev->physical_device->memory_properties
.memoryTypes[mem_vk->memory_type_index];
VkMemoryPropertyFlags flags = mem_type->propertyFlags;
/* For external allocation handles, it's possible scenario when requested
* non-mappable memory. To make sure that virtio-gpu driver will send to
* the host the address of allocated blob using RESOURCE_MAP_BLOB command
* a flag VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT must be set.
*/
if (mem_vk->export_handle_types)
flags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
VkResult result = vn_renderer_bo_create_from_device_memory(
dev->renderer, mem_vk->size, mem->base.id, flags,
mem_vk->export_handle_types, &mem->base_bo);
if (result != VK_SUCCESS) {
return result;
}
const VkImportMemoryResourceInfoMESA import_memory_resource_info = {
.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_RESOURCE_INFO_MESA,
.pNext = alloc_info->pNext,
.resourceId = mem->base_bo->res_id,
};
const VkMemoryAllocateInfo memory_allocate_info = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = &import_memory_resource_info,
.allocationSize = alloc_info->allocationSize,
.memoryTypeIndex = alloc_info->memoryTypeIndex,
};
vn_ring_roundtrip(dev->primary_ring);
result = vn_device_memory_alloc_simple(dev, mem, &memory_allocate_info);
if (result != VK_SUCCESS) {
vn_renderer_bo_unref(dev->renderer, mem->base_bo);
return result;
}
return VK_SUCCESS;
}
static VkResult
vn_device_memory_alloc_export(struct vn_device *dev,
struct vn_device_memory *mem,
const VkMemoryAllocateInfo *alloc_info)
{
VkResult result = vn_device_memory_alloc_simple(dev, mem, alloc_info);
if (result != VK_SUCCESS)
return result;
result = vn_device_memory_bo_init(dev, mem);
if (result != VK_SUCCESS) {
vn_device_memory_free_simple(dev, mem);
return result;
}
result =
vn_ring_submit_roundtrip(dev->primary_ring, &mem->bo_roundtrip_seqno);
if (result != VK_SUCCESS) {
vn_renderer_bo_unref(dev->renderer, mem->base_bo);
vn_device_memory_free_simple(dev, mem);
return result;
}
mem->bo_roundtrip_seqno_valid = true;
return VK_SUCCESS;
}
struct vn_device_memory_alloc_info {
VkMemoryAllocateInfo alloc;
VkExportMemoryAllocateInfo export;
VkMemoryAllocateFlagsInfo flags;
VkMemoryDedicatedAllocateInfo dedicated;
VkMemoryOpaqueCaptureAddressAllocateInfo capture;
};
static const VkMemoryAllocateInfo *
vn_device_memory_fix_alloc_info(
const VkMemoryAllocateInfo *alloc_info,
const VkExternalMemoryHandleTypeFlagBits renderer_handle_type,
bool has_guest_vram,
struct vn_device_memory_alloc_info *local_info)
{
local_info->alloc = *alloc_info;
VkBaseOutStructure *cur = (void *)&local_info->alloc;
vk_foreach_struct_const(src, alloc_info->pNext) {
void *next = NULL;
switch (src->sType) {
case VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO:
/* guest vram turns export alloc into import, so drop export info */
if (has_guest_vram)
break;
memcpy(&local_info->export, src, sizeof(local_info->export));
local_info->export.handleTypes = renderer_handle_type;
next = &local_info->export;
break;
case VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO:
memcpy(&local_info->flags, src, sizeof(local_info->flags));
next = &local_info->flags;
break;
case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO:
memcpy(&local_info->dedicated, src, sizeof(local_info->dedicated));
next = &local_info->dedicated;
break;
case VK_STRUCTURE_TYPE_MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO:
memcpy(&local_info->capture, src, sizeof(local_info->capture));
next = &local_info->capture;
break;
default:
break;
}
if (next) {
cur->pNext = next;
cur = next;
}
}
cur->pNext = NULL;
return &local_info->alloc;
}
static VkResult
vn_device_memory_alloc(struct vn_device *dev,
struct vn_device_memory *mem,
const VkMemoryAllocateInfo *alloc_info)
{
struct vk_device_memory *mem_vk = &mem->base.base;
const VkMemoryType *mem_type = &dev->physical_device->memory_properties
.memoryTypes[mem_vk->memory_type_index];
const bool has_guest_vram = dev->renderer->info.has_guest_vram;
const bool host_visible =
mem_type->propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
const bool export_alloc = mem_vk->export_handle_types;
const VkExternalMemoryHandleTypeFlagBits renderer_handle_type =
dev->physical_device->external_memory.renderer_handle_type;
struct vn_device_memory_alloc_info local_info;
if (mem_vk->export_handle_types &&
mem_vk->export_handle_types != renderer_handle_type) {
alloc_info = vn_device_memory_fix_alloc_info(
alloc_info, renderer_handle_type, has_guest_vram, &local_info);
/* ensure correct blob flags */
mem_vk->export_handle_types = renderer_handle_type;
}
if (has_guest_vram && (host_visible || export_alloc)) {
return vn_device_memory_alloc_guest_vram(dev, mem, alloc_info);
} else if (export_alloc) {
return vn_device_memory_alloc_export(dev, mem, alloc_info);
} else {
return vn_device_memory_alloc_simple(dev, mem, alloc_info);
}
}
static void
vn_device_memory_emit_report(struct vn_device *dev,
struct vn_device_memory *mem,
bool is_alloc,
VkResult result)
{
if (likely(!dev->memory_reports))
return;
const struct vk_device_memory *mem_vk = &mem->base.base;
VkDeviceMemoryReportEventTypeEXT type;
if (result != VK_SUCCESS) {
type = VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_ALLOCATION_FAILED_EXT;
} else if (is_alloc) {
type = mem_vk->import_handle_type
? VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_IMPORT_EXT
: VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_ALLOCATE_EXT;
} else {
type = mem_vk->import_handle_type
? VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_UNIMPORT_EXT
: VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_FREE_EXT;
}
const uint64_t mem_obj_id =
(mem_vk->import_handle_type | mem_vk->export_handle_types)
? mem->base_bo->res_id
: mem->base.id;
const VkMemoryType *mem_type = &dev->physical_device->memory_properties
.memoryTypes[mem_vk->memory_type_index];
vn_device_emit_device_memory_report(dev, type, mem_obj_id, mem_vk->size,
VK_OBJECT_TYPE_DEVICE_MEMORY,
(uintptr_t)mem, mem_type->heapIndex);
}
VkResult
vn_AllocateMemory(VkDevice device,
const VkMemoryAllocateInfo *pAllocateInfo,
const VkAllocationCallbacks *pAllocator,
VkDeviceMemory *pMemory)
{
struct vn_device *dev = vn_device_from_handle(device);
/* see vn_physical_device_init_memory_properties */
VkMemoryAllocateInfo local_info;
if (pAllocateInfo->memoryTypeIndex ==
dev->physical_device->incoherent_cached) {
local_info = *pAllocateInfo;
local_info.memoryTypeIndex = dev->physical_device->coherent_uncached;
pAllocateInfo = &local_info;
}
const VkImportMemoryFdInfoKHR *import_fd_info = NULL;
const VkMemoryDedicatedAllocateInfo *dedicated_info = NULL;
vk_foreach_struct_const(pnext, pAllocateInfo->pNext) {
switch (pnext->sType) {
case VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR:
import_fd_info = (const void *)pnext;
break;
case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO:
dedicated_info = (const void *)pnext;
break;
default:
break;
}
}
struct vn_device_memory *mem = vk_device_memory_create(
&dev->base.base, pAllocateInfo, pAllocator, sizeof(*mem));
if (!mem)
return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
vn_object_set_id(mem, vn_get_next_obj_id(), VK_OBJECT_TYPE_DEVICE_MEMORY);
VkResult result;
if (mem->base.base.ahardware_buffer) {
result = vn_android_device_import_ahb(dev, mem, dedicated_info);
} else if (import_fd_info) {
result = vn_device_memory_import_dma_buf(dev, mem, pAllocateInfo, false,
import_fd_info->fd);
} else {
result = vn_device_memory_alloc(dev, mem, pAllocateInfo);
}
vn_device_memory_emit_report(dev, mem, /* is_alloc */ true, result);
if (result != VK_SUCCESS) {
vk_device_memory_destroy(&dev->base.base, pAllocator, &mem->base.base);
return vn_error(dev->instance, result);
}
*pMemory = vn_device_memory_to_handle(mem);
return VK_SUCCESS;
}
void
vn_FreeMemory(VkDevice device,
VkDeviceMemory memory,
const VkAllocationCallbacks *pAllocator)
{
struct vn_device *dev = vn_device_from_handle(device);
struct vn_device_memory *mem = vn_device_memory_from_handle(memory);
if (!mem)
return;
vn_device_memory_emit_report(dev, mem, /* is_alloc */ false, VK_SUCCESS);
/* ensure renderer side import still sees the resource */
vn_device_memory_bo_fini(dev, mem);
if (mem->bo_roundtrip_seqno_valid)
vn_ring_wait_roundtrip(dev->primary_ring, mem->bo_roundtrip_seqno);
vn_device_memory_free_simple(dev, mem);
vk_device_memory_destroy(&dev->base.base, pAllocator, &mem->base.base);
}
uint64_t
vn_GetDeviceMemoryOpaqueCaptureAddress(
VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo *pInfo)
{
struct vn_device *dev = vn_device_from_handle(device);
return vn_call_vkGetDeviceMemoryOpaqueCaptureAddress(dev->primary_ring,
device, pInfo);
}
VkResult
vn_MapMemory(VkDevice device,
VkDeviceMemory memory,
VkDeviceSize offset,
VkDeviceSize size,
VkMemoryMapFlags flags,
void **ppData)
{
VN_TRACE_FUNC();
struct vn_device *dev = vn_device_from_handle(device);
struct vn_device_memory *mem = vn_device_memory_from_handle(memory);
const struct vk_device_memory *mem_vk = &mem->base.base;
const bool need_bo = !mem->base_bo;
void *ptr = NULL;
VkResult result;
/* We don't want to blindly create a bo for each HOST_VISIBLE memory as
* that has a cost. By deferring bo creation until now, we can avoid the
* cost unless a bo is really needed. However, that means
* vn_renderer_bo_map will block until the renderer creates the resource
* and injects the pages into the guest.
*
* XXX We also assume that a vn_renderer_bo can be created as long as the
* renderer VkDeviceMemory has a mappable memory type. That is plain
* wrong. It is impossible to fix though until some new extension is
* created and supported by the driver, and that the renderer switches to
* the extension.
*/
if (need_bo) {
result = vn_device_memory_bo_init(dev, mem);
if (result != VK_SUCCESS)
return vn_error(dev->instance, result);
}
ptr = vn_renderer_bo_map(dev->renderer, mem->base_bo);
if (!ptr) {
/* vn_renderer_bo_map implies a roundtrip on success, but not here. */
if (need_bo) {
result = vn_ring_submit_roundtrip(dev->primary_ring,
&mem->bo_roundtrip_seqno);
if (result != VK_SUCCESS)
return vn_error(dev->instance, result);
mem->bo_roundtrip_seqno_valid = true;
}
return vn_error(dev->instance, VK_ERROR_MEMORY_MAP_FAILED);
}
mem->map_end = size == VK_WHOLE_SIZE ? mem_vk->size : offset + size;
*ppData = ptr + offset;
return VK_SUCCESS;
}
void
vn_UnmapMemory(VkDevice device, VkDeviceMemory memory)
{
}
VkResult
vn_FlushMappedMemoryRanges(VkDevice device,
uint32_t memoryRangeCount,
const VkMappedMemoryRange *pMemoryRanges)
{
struct vn_device *dev = vn_device_from_handle(device);
for (uint32_t i = 0; i < memoryRangeCount; i++) {
const VkMappedMemoryRange *range = &pMemoryRanges[i];
struct vn_device_memory *mem =
vn_device_memory_from_handle(range->memory);
const VkDeviceSize size = range->size == VK_WHOLE_SIZE
? mem->map_end - range->offset
: range->size;
vn_renderer_bo_flush(dev->renderer, mem->base_bo, range->offset, size);
}
return VK_SUCCESS;
}
VkResult
vn_InvalidateMappedMemoryRanges(VkDevice device,
uint32_t memoryRangeCount,
const VkMappedMemoryRange *pMemoryRanges)
{
struct vn_device *dev = vn_device_from_handle(device);
for (uint32_t i = 0; i < memoryRangeCount; i++) {
const VkMappedMemoryRange *range = &pMemoryRanges[i];
struct vn_device_memory *mem =
vn_device_memory_from_handle(range->memory);
const VkDeviceSize size = range->size == VK_WHOLE_SIZE
? mem->map_end - range->offset
: range->size;
vn_renderer_bo_invalidate(dev->renderer, mem->base_bo, range->offset,
size);
}
return VK_SUCCESS;
}
void
vn_GetDeviceMemoryCommitment(VkDevice device,
VkDeviceMemory memory,
VkDeviceSize *pCommittedMemoryInBytes)
{
struct vn_device *dev = vn_device_from_handle(device);
vn_call_vkGetDeviceMemoryCommitment(dev->primary_ring, device, memory,
pCommittedMemoryInBytes);
}
VkResult
vn_GetMemoryFdKHR(VkDevice device,
const VkMemoryGetFdInfoKHR *pGetFdInfo,
int *pFd)
{
VN_TRACE_FUNC();
struct vn_device *dev = vn_device_from_handle(device);
struct vn_device_memory *mem =
vn_device_memory_from_handle(pGetFdInfo->memory);
/* At the moment, we support only the below handle types. */
assert(pGetFdInfo->handleType &
(VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT |
VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT));
assert(mem->base_bo);
*pFd = vn_renderer_bo_export_dma_buf(dev->renderer, mem->base_bo);
if (*pFd < 0)
return vn_error(dev->instance, VK_ERROR_TOO_MANY_OBJECTS);
return VK_SUCCESS;
}
VkResult
vn_get_memory_dma_buf_properties(struct vn_device *dev,
int fd,
uint64_t *out_alloc_size,
uint32_t *out_mem_type_bits)
{
VkDevice device = vn_device_to_handle(dev);
struct vn_renderer_bo *bo;
VkResult result = vn_renderer_bo_create_from_dma_buf(
dev->renderer, 0 /* size */, fd, 0 /* flags */, &bo);
if (result != VK_SUCCESS)
return result;
vn_ring_roundtrip(dev->primary_ring);
VkMemoryResourceAllocationSizePropertiesMESA alloc_size_props = {
.sType =
VK_STRUCTURE_TYPE_MEMORY_RESOURCE_ALLOCATION_SIZE_PROPERTIES_MESA,
};
VkMemoryResourcePropertiesMESA props = {
.sType = VK_STRUCTURE_TYPE_MEMORY_RESOURCE_PROPERTIES_MESA,
.pNext = &alloc_size_props,
};
result = vn_call_vkGetMemoryResourcePropertiesMESA(
dev->primary_ring, device, bo->res_id, &props);
vn_renderer_bo_unref(dev->renderer, bo);
if (result != VK_SUCCESS)
return result;
*out_alloc_size = alloc_size_props.allocationSize;
*out_mem_type_bits = props.memoryTypeBits;
return VK_SUCCESS;
}
VkResult
vn_GetMemoryFdPropertiesKHR(VkDevice device,
VkExternalMemoryHandleTypeFlagBits handleType,
int fd,
VkMemoryFdPropertiesKHR *pMemoryFdProperties)
{
VN_TRACE_FUNC();
struct vn_device *dev = vn_device_from_handle(device);
uint64_t alloc_size = 0;
uint32_t mem_type_bits = 0;
VkResult result = VK_SUCCESS;
if (handleType != VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT)
return vn_error(dev->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
result =
vn_get_memory_dma_buf_properties(dev, fd, &alloc_size, &mem_type_bits);
if (result != VK_SUCCESS)
return vn_error(dev->instance, result);
pMemoryFdProperties->memoryTypeBits = mem_type_bits;
return VK_SUCCESS;
}