首页 > 解决方案 > 是否可以通过覆盖 PFN_vkAllocationFunction 并将回调传递给 Vulkan 函数来分配主机内存

问题描述

我已经实现了一个自定义分配器来跟踪由vkAllocateMemory. 我还研究了有关此主题的官方 Khronos 文档,但我找不到以下问题的答案:

  1. 究竟是什么size参数PFN_vkAllocationFunction?我发现显然这不是我们要为其分配内存的实际缓冲区的大小。我怀疑这是 Vulkan 结构或任何其他 Vulkan 内部缓冲区的大小。无论我想分配多大的内存块,它的大小总是设置为200(它是机器/GPU/驱动程序相关的值,但它是一个常量值)。出于测试目的,我使用了来自:https ://github.com/SaschaWillems/Vulkan 的三角形和来自类似问题的分配器:Vulkan's VkAllocationCallbacks implementation with malloc/free()
triangle before vkAllocateMemory: memAlloc.allocationSize: 7864320 sizeof(memAlloc): 32
triangle after vkAllocateMemory: memAlloc.allocationSize: 7864320
pAllocator's allocationFunction: <Memory>, size: 200, alignment: 8, allocationScope: 1, return ptr* : 0x0x5564ac917b20

我还发现对于其他调用,这size对于不同的 vulkan 调用是不同的,比如vkCreateRenderPassor vkCreateImageView

pAllocator's allocationFunction: <ImageView>, size: 160, alignment: 8, allocationScope: 1, return ptr* : 0x0x5564acdf8390 
pAllocator's allocationFunction: <ImageView>, size: 824, alignment: 8, allocationScope: 1, return ptr* : 0x0x5564acdf8440 
pAllocator's allocationFunction: <RenderPass>, size: 200, alignment: 8, allocationScope: 1, return ptr* : 0x0x5564ac950ee0 
pAllocator's allocationFunction: <RenderPass>, size: 88, alignment: 8, allocationScope: 1, return ptr* : 0x0x5564acdf8780 
pAllocator's allocationFunction: <RenderPass>, size: 56, alignment: 8, allocationScope: 1, return ptr* : 0x0x5564acdf7a00 
pAllocator's allocationFunction: <RenderPass>, size: 344, alignment: 8, allocationScope: 1, return ptr* : 0x0x5564ac6a07c0 
pAllocator's allocationFunction: <RenderPass>, size: 8, alignment: 8, allocationScope: 1, return ptr* : 0x0x5564acdf87e0 
  1. 是否可以使用这些回调分配主机可见内存?我想模仿VK_EXT_external_memory_host,VK_KHR_external_memory_fd或者VK_EXT_external_memory_dma_buf 但我不知道这种方法(即实现自己的分配器)是否对它有用。

标签: memorymemory-managementcallbackvulkan

解决方案


要了解发生了什么,您需要了解VkAllocationCallbacks是为了什么。所以让我们潜入水中。

例如,当您调用vkCreateDevice时,Vulkan 实现需要返回一个VkDevice句柄。这无疑是一个指向对象的指针。那么……它的记忆是从哪里来的?必须有一些内部的、实现定义的数据结构在起作用。这可能需要堆分配。

但是,对于低级系统,在使用该系统的应用程序不知情或不同意的情况下,随意分配堆内存通常被认为是不礼貌的。出于多种原因,较大的应用程序可能希望专门为 Vulkan 实现预先分配大量内存。

能够做到这一点需要能够用您自己的替换Vulkan 实现的堆分配函数。这就是VkAllocationCallbacks目的:允许您的程序为 Vulkan 实现提供堆分配服务,Vulkan 实现会将它们用于自己的内部数据结构。最后一部分很重要:这些分配是供内部使用的。

vkAllocateMemory是用于分配设备可访问内存的函数。现在是的,根据使用的内存类型,它可能从与 CPU 可访问内存相同的内存池中分配。但是分配的内存vkAllocateMemory不是用于 Vulkan 实现的;它供您的应用程序使用,通过 Vulkan API 进行调解。

现在,上面我说“在某些情况下”。当您使用 创建设备VkAllocationCallbacks时,回调将用于为使用该设备的任何 Vulkan 函数分配实现内存。这就是那些回调的上下文。其他函数有自己的上下文。例如,当你调用 时vkCreateDescriptorPool,你也给它回调。如果指定,这些回调将用于描述符池的任何分配。

现在,如上所述,vkAllocateMemory分配设备可访问的内存。但是,它也会创建一个VkDeviceMemory表示此存储的对象。该对象位于 CPU 空间中,因此它必须是从 CPU 存储中分配的。

这就是您看到的 200 字节所代表的含义:不是设备内存分配本身,而是用于管理该设备内存的 CPU 存储分配。可以说,由 表示的内部实现对象VkDeviceMemory占用了 200 个字节。

总体而言,您无法通过旨在让您访问 Vulkan 分配的 CPU 可访问内存的系统来跟踪设备可访问分配。


推荐阅读