mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-22 13:08:09 +02:00
Add support of GUEST_VRAM type of blob. These are dedicated heap memory allocations required for vk support on hypervisors that don't support runtime injections of host memory into guest physical address space. The flow of usage: 1) Host VM reserves dedicated heap memory 2) Device get info about memory reservations and report it to guest using mmio registers 3) Guest virtio-gpu driver on starts checks mmio registers for physical address and length of reserved region. Then it reserves it in guest. 4) On each call of vkAllocateMemory() guest driver gets chunk of required memory and send it to host using sg list. It uses one sg entry for 1 blob call. Heap is managed on guest using drm memory manager (drm_mm). Signed-off-by: Oleksandr.Gabrylchuk <Oleksandr.Gabrylchuk@opensynergy.com> Signed-off-by: Andrii Pauk <Andrii.Pauk@opensynergy.com> Reviewed-by: Yiwei Zhang <zzyiwei@chromium.org> Reviewed-by: Chia-I Wu <olvaffe@gmail.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/14536>
730 lines
24 KiB
C
730 lines
24 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"
|
|
|
|
/* device memory commands */
|
|
|
|
static VkResult
|
|
vn_device_memory_pool_grow_alloc(struct vn_device *dev,
|
|
uint32_t mem_type_index,
|
|
VkDeviceSize size,
|
|
struct vn_device_memory **out_mem)
|
|
{
|
|
VkDevice dev_handle = vn_device_to_handle(dev);
|
|
const VkAllocationCallbacks *alloc = &dev->base.base.alloc;
|
|
const VkPhysicalDeviceMemoryProperties *mem_props =
|
|
&dev->physical_device->memory_properties.memoryProperties;
|
|
const VkMemoryPropertyFlags mem_flags =
|
|
mem_props->memoryTypes[mem_type_index].propertyFlags;
|
|
struct vn_device_memory *mem = NULL;
|
|
VkDeviceMemory mem_handle = VK_NULL_HANDLE;
|
|
VkResult result;
|
|
|
|
mem = vk_zalloc(alloc, sizeof(*mem), VN_DEFAULT_ALIGN,
|
|
VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
|
|
if (!mem)
|
|
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
|
|
|
vn_object_base_init(&mem->base, VK_OBJECT_TYPE_DEVICE_MEMORY, &dev->base);
|
|
mem->size = size;
|
|
mem->flags = mem_flags;
|
|
|
|
mem_handle = vn_device_memory_to_handle(mem);
|
|
result = vn_call_vkAllocateMemory(
|
|
dev->instance, dev_handle,
|
|
&(const VkMemoryAllocateInfo){
|
|
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
|
.allocationSize = size,
|
|
.memoryTypeIndex = mem_type_index,
|
|
},
|
|
NULL, &mem_handle);
|
|
if (result != VK_SUCCESS) {
|
|
mem_handle = VK_NULL_HANDLE;
|
|
goto fail;
|
|
}
|
|
|
|
result = vn_renderer_bo_create_from_device_memory(
|
|
dev->renderer, mem->size, mem->base.id, mem->flags, 0, &mem->base_bo);
|
|
if (result != VK_SUCCESS) {
|
|
assert(!mem->base_bo);
|
|
goto fail;
|
|
}
|
|
|
|
result =
|
|
vn_instance_submit_roundtrip(dev->instance, &mem->bo_roundtrip_seqno);
|
|
if (result != VK_SUCCESS)
|
|
goto fail;
|
|
|
|
mem->bo_roundtrip_seqno_valid = true;
|
|
*out_mem = mem;
|
|
|
|
return VK_SUCCESS;
|
|
|
|
fail:
|
|
if (mem->base_bo)
|
|
vn_renderer_bo_unref(dev->renderer, mem->base_bo);
|
|
if (mem_handle != VK_NULL_HANDLE)
|
|
vn_async_vkFreeMemory(dev->instance, dev_handle, mem_handle, NULL);
|
|
vn_object_base_fini(&mem->base);
|
|
vk_free(alloc, mem);
|
|
return result;
|
|
}
|
|
|
|
static struct vn_device_memory *
|
|
vn_device_memory_pool_ref(struct vn_device *dev,
|
|
struct vn_device_memory *pool_mem)
|
|
{
|
|
assert(pool_mem->base_bo);
|
|
|
|
vn_renderer_bo_ref(dev->renderer, pool_mem->base_bo);
|
|
|
|
return pool_mem;
|
|
}
|
|
|
|
static void
|
|
vn_device_memory_pool_unref(struct vn_device *dev,
|
|
struct vn_device_memory *pool_mem)
|
|
{
|
|
const VkAllocationCallbacks *alloc = &dev->base.base.alloc;
|
|
|
|
assert(pool_mem->base_bo);
|
|
|
|
if (!vn_renderer_bo_unref(dev->renderer, pool_mem->base_bo))
|
|
return;
|
|
|
|
/* wait on valid bo_roundtrip_seqno before vkFreeMemory */
|
|
if (pool_mem->bo_roundtrip_seqno_valid)
|
|
vn_instance_wait_roundtrip(dev->instance, pool_mem->bo_roundtrip_seqno);
|
|
|
|
vn_async_vkFreeMemory(dev->instance, vn_device_to_handle(dev),
|
|
vn_device_memory_to_handle(pool_mem), NULL);
|
|
vn_object_base_fini(&pool_mem->base);
|
|
vk_free(alloc, pool_mem);
|
|
}
|
|
|
|
void
|
|
vn_device_memory_pool_fini(struct vn_device *dev, uint32_t mem_type_index)
|
|
{
|
|
struct vn_device_memory_pool *pool = &dev->memory_pools[mem_type_index];
|
|
if (pool->memory)
|
|
vn_device_memory_pool_unref(dev, pool->memory);
|
|
mtx_destroy(&pool->mutex);
|
|
}
|
|
|
|
static VkResult
|
|
vn_device_memory_pool_grow_locked(struct vn_device *dev,
|
|
uint32_t mem_type_index,
|
|
VkDeviceSize size)
|
|
{
|
|
struct vn_device_memory *mem;
|
|
VkResult result =
|
|
vn_device_memory_pool_grow_alloc(dev, mem_type_index, size, &mem);
|
|
if (result != VK_SUCCESS)
|
|
return result;
|
|
|
|
struct vn_device_memory_pool *pool = &dev->memory_pools[mem_type_index];
|
|
if (pool->memory)
|
|
vn_device_memory_pool_unref(dev, pool->memory);
|
|
|
|
pool->memory = mem;
|
|
pool->used = 0;
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
static VkResult
|
|
vn_device_memory_pool_suballocate(struct vn_device *dev,
|
|
struct vn_device_memory *mem,
|
|
uint32_t mem_type_index)
|
|
{
|
|
const VkDeviceSize pool_size = 16 * 1024 * 1024;
|
|
/* XXX We don't know the alignment requirement. Use 64K because some GPUs
|
|
* have 64K pages. It is also required by newer Intel GPUs. But really we
|
|
* should require kernel 5.12+, where there is no KVM memslot limit, and
|
|
* remove this whole thing.
|
|
*/
|
|
const VkDeviceSize pool_align = 64 * 1024;
|
|
struct vn_device_memory_pool *pool = &dev->memory_pools[mem_type_index];
|
|
|
|
assert(mem->size <= pool_size);
|
|
|
|
mtx_lock(&pool->mutex);
|
|
|
|
if (!pool->memory || pool->used + mem->size > pool_size) {
|
|
VkResult result =
|
|
vn_device_memory_pool_grow_locked(dev, mem_type_index, pool_size);
|
|
if (result != VK_SUCCESS) {
|
|
mtx_unlock(&pool->mutex);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
mem->base_memory = vn_device_memory_pool_ref(dev, pool->memory);
|
|
|
|
/* point mem->base_bo at pool base_bo and assign base_offset accordingly */
|
|
mem->base_bo = pool->memory->base_bo;
|
|
mem->base_offset = pool->used;
|
|
pool->used += align64(mem->size, pool_align);
|
|
|
|
mtx_unlock(&pool->mutex);
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
static bool
|
|
vn_device_memory_should_suballocate(const struct vn_device *dev,
|
|
const VkMemoryAllocateInfo *alloc_info,
|
|
const VkMemoryPropertyFlags flags)
|
|
{
|
|
const struct vn_instance *instance = dev->physical_device->instance;
|
|
const struct vn_renderer_info *renderer = &instance->renderer->info;
|
|
|
|
if (renderer->has_guest_vram)
|
|
return false;
|
|
|
|
/* We should not support suballocations because apps can do better. But
|
|
* each BO takes up a KVM memslot currently and some CTS tests exhausts
|
|
* them. This might not be needed on newer (host) kernels where there are
|
|
* many more KVM memslots.
|
|
*/
|
|
|
|
/* consider host-visible memory only */
|
|
if (!(flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT))
|
|
return false;
|
|
|
|
/* reject larger allocations */
|
|
if (alloc_info->allocationSize > 64 * 1024)
|
|
return false;
|
|
|
|
/* reject if there is any pnext struct other than
|
|
* VkMemoryDedicatedAllocateInfo, or if dedicated allocation is required
|
|
*/
|
|
if (alloc_info->pNext) {
|
|
const VkMemoryDedicatedAllocateInfo *dedicated = alloc_info->pNext;
|
|
if (dedicated->sType !=
|
|
VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO ||
|
|
dedicated->pNext)
|
|
return false;
|
|
|
|
const struct vn_image *img = vn_image_from_handle(dedicated->image);
|
|
if (img) {
|
|
for (uint32_t i = 0; i < ARRAY_SIZE(img->requirements); i++) {
|
|
if (img->requirements[i].dedicated.requiresDedicatedAllocation)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const struct vn_buffer *buf = vn_buffer_from_handle(dedicated->buffer);
|
|
if (buf && buf->requirements.dedicated.requiresDedicatedAllocation)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
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)
|
|
{
|
|
VkDevice device = vn_device_to_handle(dev);
|
|
VkDeviceMemory memory = vn_device_memory_to_handle(mem);
|
|
const VkPhysicalDeviceMemoryProperties *mem_props =
|
|
&dev->physical_device->memory_properties.memoryProperties;
|
|
VkMemoryPropertyFlags mem_flags =
|
|
mem_props->memoryTypes[alloc_info->memoryTypeIndex].propertyFlags;
|
|
struct vn_renderer_bo *bo;
|
|
VkResult result = VK_SUCCESS;
|
|
|
|
if (force_unmappable)
|
|
mem_flags &= ~VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
|
|
|
|
result = vn_renderer_bo_create_from_dma_buf(
|
|
dev->renderer, alloc_info->allocationSize, fd, mem_flags, &bo);
|
|
if (result != VK_SUCCESS)
|
|
return result;
|
|
|
|
vn_instance_roundtrip(dev->instance);
|
|
|
|
/* XXX fix VkImportMemoryResourceInfoMESA to support memory planes */
|
|
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_call_vkAllocateMemory(dev->instance, device,
|
|
&memory_allocate_info, NULL, &memory);
|
|
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,
|
|
VkExternalMemoryHandleTypeFlags external_handles)
|
|
{
|
|
VkDevice dev_handle = vn_device_to_handle(dev);
|
|
VkDeviceMemory mem_handle = vn_device_memory_to_handle(mem);
|
|
VkResult result = VK_SUCCESS;
|
|
|
|
result = vn_renderer_bo_create_from_device_memory(
|
|
dev->renderer, mem->size, 0, mem->flags, external_handles,
|
|
&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_instance_roundtrip(dev->instance);
|
|
|
|
result = vn_call_vkAllocateMemory(
|
|
dev->instance, dev_handle, &memory_allocate_info, NULL, &mem_handle);
|
|
if (result != VK_SUCCESS) {
|
|
vn_renderer_bo_unref(dev->renderer, mem->base_bo);
|
|
return result;
|
|
}
|
|
|
|
result =
|
|
vn_instance_submit_roundtrip(dev->instance, &mem->bo_roundtrip_seqno);
|
|
if (result != VK_SUCCESS) {
|
|
vn_renderer_bo_unref(dev->renderer, mem->base_bo);
|
|
vn_async_vkFreeMemory(dev->instance, dev_handle, mem_handle, NULL);
|
|
return result;
|
|
}
|
|
|
|
mem->bo_roundtrip_seqno_valid = true;
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
static VkResult
|
|
vn_device_memory_alloc_generic(
|
|
struct vn_device *dev,
|
|
struct vn_device_memory *mem,
|
|
const VkMemoryAllocateInfo *alloc_info,
|
|
VkExternalMemoryHandleTypeFlags external_handles)
|
|
{
|
|
VkDevice dev_handle = vn_device_to_handle(dev);
|
|
VkDeviceMemory mem_handle = vn_device_memory_to_handle(mem);
|
|
VkResult result = VK_SUCCESS;
|
|
|
|
result = vn_call_vkAllocateMemory(dev->instance, dev_handle, alloc_info,
|
|
NULL, &mem_handle);
|
|
if (result != VK_SUCCESS || !external_handles)
|
|
return result;
|
|
|
|
result = vn_renderer_bo_create_from_device_memory(
|
|
dev->renderer, mem->size, mem->base.id, mem->flags, external_handles,
|
|
&mem->base_bo);
|
|
if (result != VK_SUCCESS) {
|
|
vn_async_vkFreeMemory(dev->instance, dev_handle, mem_handle, NULL);
|
|
return result;
|
|
}
|
|
|
|
result =
|
|
vn_instance_submit_roundtrip(dev->instance, &mem->bo_roundtrip_seqno);
|
|
if (result != VK_SUCCESS) {
|
|
vn_renderer_bo_unref(dev->renderer, mem->base_bo);
|
|
vn_async_vkFreeMemory(dev->instance, dev_handle, mem_handle, NULL);
|
|
return result;
|
|
}
|
|
|
|
mem->bo_roundtrip_seqno_valid = true;
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
static VkResult
|
|
vn_device_memory_alloc(struct vn_device *dev,
|
|
struct vn_device_memory *mem,
|
|
const VkMemoryAllocateInfo *alloc_info,
|
|
VkExternalMemoryHandleTypeFlags external_handles)
|
|
{
|
|
const struct vn_instance *instance = dev->physical_device->instance;
|
|
const struct vn_renderer_info *renderer_info = &instance->renderer->info;
|
|
|
|
if (renderer_info->has_guest_vram) {
|
|
return vn_device_memory_alloc_guest_vram(dev, mem, alloc_info,
|
|
external_handles);
|
|
}
|
|
|
|
return vn_device_memory_alloc_generic(dev, mem, alloc_info,
|
|
external_handles);
|
|
}
|
|
|
|
VkResult
|
|
vn_AllocateMemory(VkDevice device,
|
|
const VkMemoryAllocateInfo *pAllocateInfo,
|
|
const VkAllocationCallbacks *pAllocator,
|
|
VkDeviceMemory *pMemory)
|
|
{
|
|
struct vn_device *dev = vn_device_from_handle(device);
|
|
const VkAllocationCallbacks *alloc =
|
|
pAllocator ? pAllocator : &dev->base.base.alloc;
|
|
|
|
const VkPhysicalDeviceMemoryProperties *mem_props =
|
|
&dev->physical_device->memory_properties.memoryProperties;
|
|
const VkMemoryPropertyFlags mem_flags =
|
|
mem_props->memoryTypes[pAllocateInfo->memoryTypeIndex].propertyFlags;
|
|
|
|
const VkExportMemoryAllocateInfo *export_info = NULL;
|
|
const VkImportAndroidHardwareBufferInfoANDROID *import_ahb_info = NULL;
|
|
const VkImportMemoryFdInfoKHR *import_fd_info = NULL;
|
|
bool export_ahb = false;
|
|
|
|
vk_foreach_struct_const(pnext, pAllocateInfo->pNext) {
|
|
switch (pnext->sType) {
|
|
case VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO:
|
|
export_info = (void *)pnext;
|
|
if (export_info->handleTypes &
|
|
VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID)
|
|
export_ahb = true;
|
|
else if (!export_info->handleTypes)
|
|
export_info = NULL;
|
|
break;
|
|
case VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID:
|
|
import_ahb_info = (void *)pnext;
|
|
break;
|
|
case VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR:
|
|
import_fd_info = (void *)pnext;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
struct vn_device_memory *mem =
|
|
vk_zalloc(alloc, sizeof(*mem), VN_DEFAULT_ALIGN,
|
|
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
|
|
if (!mem)
|
|
return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
|
|
|
|
vn_object_base_init(&mem->base, VK_OBJECT_TYPE_DEVICE_MEMORY, &dev->base);
|
|
mem->size = pAllocateInfo->allocationSize;
|
|
mem->flags = mem_flags;
|
|
|
|
VkDeviceMemory mem_handle = vn_device_memory_to_handle(mem);
|
|
VkResult result;
|
|
if (import_ahb_info) {
|
|
result = vn_android_device_import_ahb(dev, mem, pAllocateInfo, alloc,
|
|
import_ahb_info->buffer, false);
|
|
} else if (export_ahb) {
|
|
result = vn_android_device_allocate_ahb(dev, mem, pAllocateInfo, alloc);
|
|
} else if (import_fd_info) {
|
|
result = vn_device_memory_import_dma_buf(dev, mem, pAllocateInfo, false,
|
|
import_fd_info->fd);
|
|
} else if (export_info) {
|
|
result = vn_device_memory_alloc(dev, mem, pAllocateInfo,
|
|
export_info->handleTypes);
|
|
} else if (vn_device_memory_should_suballocate(dev, pAllocateInfo,
|
|
mem_flags)) {
|
|
result = vn_device_memory_pool_suballocate(
|
|
dev, mem, pAllocateInfo->memoryTypeIndex);
|
|
} else {
|
|
result = vn_device_memory_alloc(dev, mem, pAllocateInfo, 0);
|
|
}
|
|
if (result != VK_SUCCESS) {
|
|
vn_object_base_fini(&mem->base);
|
|
vk_free(alloc, mem);
|
|
return vn_error(dev->instance, result);
|
|
}
|
|
|
|
*pMemory = mem_handle;
|
|
|
|
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);
|
|
const VkAllocationCallbacks *alloc =
|
|
pAllocator ? pAllocator : &dev->base.base.alloc;
|
|
|
|
if (!mem)
|
|
return;
|
|
|
|
if (mem->base_memory) {
|
|
vn_device_memory_pool_unref(dev, mem->base_memory);
|
|
} else {
|
|
if (mem->base_bo)
|
|
vn_renderer_bo_unref(dev->renderer, mem->base_bo);
|
|
|
|
if (mem->bo_roundtrip_seqno_valid)
|
|
vn_instance_wait_roundtrip(dev->instance, mem->bo_roundtrip_seqno);
|
|
|
|
vn_async_vkFreeMemory(dev->instance, device, memory, NULL);
|
|
}
|
|
|
|
if (mem->ahb)
|
|
vn_android_release_ahb(mem->ahb);
|
|
|
|
vn_object_base_fini(&mem->base);
|
|
vk_free(alloc, mem);
|
|
}
|
|
|
|
uint64_t
|
|
vn_GetDeviceMemoryOpaqueCaptureAddress(
|
|
VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo *pInfo)
|
|
{
|
|
struct vn_device *dev = vn_device_from_handle(device);
|
|
ASSERTED struct vn_device_memory *mem =
|
|
vn_device_memory_from_handle(pInfo->memory);
|
|
|
|
assert(!mem->base_memory);
|
|
return vn_call_vkGetDeviceMemoryOpaqueCaptureAddress(dev->instance, device,
|
|
pInfo);
|
|
}
|
|
|
|
VkResult
|
|
vn_MapMemory(VkDevice device,
|
|
VkDeviceMemory memory,
|
|
VkDeviceSize offset,
|
|
VkDeviceSize size,
|
|
VkMemoryMapFlags flags,
|
|
void **ppData)
|
|
{
|
|
struct vn_device *dev = vn_device_from_handle(device);
|
|
struct vn_device_memory *mem = vn_device_memory_from_handle(memory);
|
|
const bool need_bo = !mem->base_bo;
|
|
void *ptr = NULL;
|
|
VkResult result;
|
|
|
|
assert(mem->flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
|
|
|
|
/* 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.
|
|
*/
|
|
if (need_bo) {
|
|
result = vn_renderer_bo_create_from_device_memory(
|
|
dev->renderer, mem->size, mem->base.id, mem->flags, 0,
|
|
&mem->base_bo);
|
|
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_instance_submit_roundtrip(dev->instance,
|
|
&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->size : offset + size;
|
|
|
|
*ppData = ptr + mem->base_offset + 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,
|
|
mem->base_offset + 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,
|
|
mem->base_offset + range->offset, size);
|
|
}
|
|
|
|
return VK_SUCCESS;
|
|
}
|
|
|
|
void
|
|
vn_GetDeviceMemoryCommitment(VkDevice device,
|
|
VkDeviceMemory memory,
|
|
VkDeviceSize *pCommittedMemoryInBytes)
|
|
{
|
|
struct vn_device *dev = vn_device_from_handle(device);
|
|
ASSERTED struct vn_device_memory *mem =
|
|
vn_device_memory_from_handle(memory);
|
|
|
|
assert(!mem->base_memory);
|
|
vn_call_vkGetDeviceMemoryCommitment(dev->instance, device, memory,
|
|
pCommittedMemoryInBytes);
|
|
}
|
|
|
|
VkResult
|
|
vn_GetMemoryFdKHR(VkDevice device,
|
|
const VkMemoryGetFdInfoKHR *pGetFdInfo,
|
|
int *pFd)
|
|
{
|
|
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_memory && 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 = NULL;
|
|
VkResult result = VK_SUCCESS;
|
|
|
|
result = vn_renderer_bo_create_from_dma_buf(dev->renderer, 0 /* size */,
|
|
fd, 0 /* flags */, &bo);
|
|
if (result != VK_SUCCESS)
|
|
return result;
|
|
|
|
vn_instance_roundtrip(dev->instance);
|
|
|
|
VkMemoryResourceAllocationSizeProperties100000MESA alloc_size_props = {
|
|
.sType =
|
|
VK_STRUCTURE_TYPE_MEMORY_RESOURCE_ALLOCATION_SIZE_PROPERTIES_100000_MESA,
|
|
.pNext = NULL,
|
|
.allocationSize = 0,
|
|
};
|
|
VkMemoryResourcePropertiesMESA props = {
|
|
.sType = VK_STRUCTURE_TYPE_MEMORY_RESOURCE_PROPERTIES_MESA,
|
|
.pNext =
|
|
dev->instance->experimental.memoryResourceAllocationSize == VK_TRUE
|
|
? &alloc_size_props
|
|
: NULL,
|
|
.memoryTypeBits = 0,
|
|
};
|
|
result = vn_call_vkGetMemoryResourcePropertiesMESA(dev->instance, 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)
|
|
{
|
|
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;
|
|
}
|