Edit

IABSD.fr/xenocara/lib/mesa/src/virtio/vulkan/vn_device_memory.c

Branch :

  • Show log

    Commit

  • Author : jsg
    Date : 2025-06-05 11:23:11
    Hash : 67d6f117
    Message : Import Mesa 25.0.7

  • lib/mesa/src/virtio/vulkan/vn_device_memory.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;
    
       /* fine to false it here since renderer submission failure is fatal */
       mem->bo_ring_seqno_valid = false;
    
       /* 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;
    
       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);
    
       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) {
          vn_log(dev->instance, "bo_create_from_dma_buf failed");
          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) {
          vn_log(dev->instance, "vkGetMemoryResourcePropertiesMESA failed");
          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;
    }