当前位置: 首页 > news >正文

Vulkan 学习(5)---- Vulkan 内存分配

目录

        • Overview
        • 枚举内存信息
        • 分配内存
        • 内存映射

Overview

Vulkan 将内存管理的工作交给了开发者自己负责,如何分配内存,如何指定内存策略都是由开发者自己决定的,当然处理问题也是由开发者自己负责的

Vulkan 将内存划分为两大类:主机内存(Host Memory)设备内存(Device Memory)

在移动设备上,主机内存就是 CPU 内存,设备内存就是GPU 显存,在此基础上,每种内存类型还可以按照属性进一步划分

Vulkan 提供了一种透明的机制来显示内存的细节和相关属性,这样做在 opengl 中是完全不可能的,后者不允许应用程序显式的控制内存区域和布局

vulkan memory

Vulkan 中系统的内存有四种类型(并不是所有设备都支持这个四种类型):

  • Host Local Memory,只对 Host 可见的内存,通常称之为普通内存
  • Device Local Memory,只对 Device 可见的内存,通常称之为显存
  • Host Local Device Memory,由 Host 管理的,对 Device 可见的内存
  • Device Local Hosy Memory,由 Device 管理的,对 Host 可见的内存

对比这两种内存类型,主机内存比设备内存更慢,但是 Host Memory 通常容量更大(也就是一般显存速度更快,但是容量更小)

设备内存,它对物理设备是直接可见的,物理设备可以直接读取其中的内存区块,设备内存和物理设备之间的关系十分紧密,因此它的性能比宿主机内存更高

VkImage,VkBuffer,以及一致性的缓存对象Uniform Buffer都是从设备内存端分配的

单一的物理设备可能有多种类型的内存,根据它们的堆类型以及属性的不同还可能进一步细分

枚举内存信息

使用 vkGetPhysicalDeviceMemoryProperties 获取设备的内存信息,其函数定义如下:

VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice                            physicalDevice,VkPhysicalDeviceMemoryProperties*           pMemoryProperties);

其中 physicalDevice 就是创建的物理设备,VkPhysicalDeviceMemoryProperties 定义如下:

typedef struct VkPhysicalDeviceMemoryProperties {uint32_t        memoryTypeCount;VkMemoryType    memoryTypes[VK_MAX_MEMORY_TYPES];uint32_t        memoryHeapCount;VkMemoryHeap    memoryHeaps[VK_MAX_MEMORY_HEAPS];
} VkPhysicalDeviceMemoryProperties;
  • memoryTypeCount 表示支持的内存类型数量
  • memoryTypes 有效元素个数为 memoryTypeCount 的内存类型信息数组
  • memoryHeapCount 表示支持的内存堆数量
  • memoryHeaps 有效元素是 memoryHeapCount 内存堆信息数组

其中 VkMemoryType 类型定义:

typedef struct VkMemoryType {VkMemoryPropertyFlags    propertyFlags;uint32_t                 heapIndex;
} VkMemoryType;
  • propertyFlags 内存类型标志位。
  • heapIndex 对应的 memoryHeaps 堆索引。

propertyFlags 有效值被定义在了 VkMemoryPropertyFlagBits 枚举中,其定义如下:

typedef enum VkMemoryPropertyFlagBits {VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT = 0x00000001,VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT = 0x00000002,VK_MEMORY_PROPERTY_HOST_COHERENT_BIT = 0x00000004,VK_MEMORY_PROPERTY_HOST_CACHED_BIT = 0x00000008,VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT = 0x00000010,VK_MEMORY_PROPERTY_PROTECTED_BIT = 0x00000020,VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD = 0x00000040,VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD = 0x00000080,VK_MEMORY_PROPERTY_RDMA_CAPABLE_BIT_NV = 0x00000100,VK_MEMORY_PROPERTY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkMemoryPropertyFlagBits;
typedef VkFlags VkMemoryPropertyFlags;
  • VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT 表示该内存设备上分配的内存可以被物理设备高效访问,只有对应的堆为 VK_MEMORY_HEAP_DEVICE_LOCAL_BIT 才会有该内存类型

  • VK_MEMORY_PROPERTY_HOST_COHERENT_BIT 表示该内存设备上分配的内存将自动进行同步,不需要手动调用 vkFlushMappedMemoryRanges()vkInvalidateMappedMemoryRanges() 来进行内存同步

  • VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT 表示在此内存类型上分配的内存可被 Host 端通过 vkMapMemory() 函数进行映射,进而进行访问

  • VK_MEMORY_PROPERTY_HOST_COHERENT_BIT 表示该内存类型上分配的内存为缓存类型,Host 端访问缓存的内存类型会比较快,但是非缓存内存总是同步内存(VK_MEMORY_PROPERTY_HOST_COHERENT_BIT )

  • VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT 表示在此内存类型上分配的内存只有物理设备可访问
    内存类型不能同时为 VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BITVK_MEMORY_PROPERTY_HOST_VISIBLE_BIT

另外的 VkMemoryHeap 的类型定义如下图所示:

typedef struct VkMemoryHeap {VkDeviceSize         size;VkMemoryHeapFlags    flags;
} VkMemoryHeap;
  • size 该堆大小, 单位为字节
  • flags 该堆类型标志位

其中的 VkMemoryHeapFlags 的类型定义如下:

typedef enum VkMemoryHeapFlagBits {VK_MEMORY_HEAP_DEVICE_LOCAL_BIT = 0x00000001,VK_MEMORY_HEAP_MULTI_INSTANCE_BIT = 0x00000002,VK_MEMORY_HEAP_MULTI_INSTANCE_BIT_KHR = VK_MEMORY_HEAP_MULTI_INSTANCE_BIT,VK_MEMORY_HEAP_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkMemoryHeapFlagBits;
typedef VkFlags VkMemoryHeapFlags;
  • VK_MEMORY_HEAP_DEVICE_LOCAL_BIT: 表示内存堆是设备本地的,这种内存通常是最快的,因为它和 GPU 紧密集成,适合存储需要频繁访问的数据

  • VK_MEMORY_HEAP_MULTI_INSTANCE_BIT: 用于多 GPU 配置,表示内存堆在多个物理设备实例中是独立的

  • VK_MEMORY_HEAP_MULTI_INSTANCE_BIT_KHRVK_MEMORY_HEAP_MULTI_INSTANCE_BIT 一致,用于兼容

打印内存信息的参考代码如下:

    // 获取物理设备内存属性VkPhysicalDeviceMemoryProperties memoryProperties;vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);std::cout << "=============================================================" << std::endl;
#if (DUMP_MEMORY_PROPS == 1)uint32_t memtypeCount = memoryProperties.memoryTypeCount;std::cout << "memoryTypeCount: " << memtypeCount << std::endl;for (uint32_t i = 0; i < memtypeCount; i++) {std::cout << "propertyFlags:" << memoryProperties.memoryTypes[i].propertyFlags << std::endl;std::cout << "heap index:" << memoryProperties.memoryTypes[i].heapIndex << std::endl;}uint32_t heapCount = memoryProperties.memoryHeapCount;std::cout << "heapCount: " << heapCount << std::endl;for (uint32_t i = 0; i < heapCount; i++) {std::cout << "flags:" << memoryProperties.memoryHeaps[i].flags << std::endl;std::cout << "size:" << memoryProperties.memoryHeaps[i].size << std::endl;}
#endif

结果如下:

memoryTypeCount: 3(BIN:011)
propertyFlags:1
heap index:0
propertyFlags:7 (BIN:0111)
heap index:0
propertyFlags:15(BIN:01111)
heap index:0
heapCount: 1
flags:1
size:17008445440
分配内存

分配内存的函数是 vkAllocateMemory,其原型如下:

VKAPI_ATTR VkResult VKAPI_CALL vkAllocateMemory(VkDevice                                    device,const VkMemoryAllocateInfo*                 pAllocateInfo,const VkAllocationCallbacks*                pAllocator,VkDeviceMemory*                             pMemory);
  • device 是用于分配的逻辑设备
  • pAllocateInfo 分配信息结构体
  • pAllocator 内存分配器,如果传入 nullptr 则表示默认的内存分配器
  • pMemory 分配出的内存结构

其中 VkMemoryAllocateInfo 的信息如下:

typedef struct VkMemoryAllocateInfo {VkStructureType    sType;const void*        pNext;VkDeviceSize       allocationSize;uint32_t           memoryTypeIndex;
} VkMemoryAllocateInfo;
  • sType 是该结构体的类型枚举值, 必须是
    VkStructureType::VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO
  • pNext 要么是 NULL 要么指向其他结构体来扩展该结构体
  • allocationSize 要分配的内存大小。单位为Bytes
  • memoryTypeIndex 分配内存的目标内存类型索引

其中 memoryTypeIndex 其重要,用于指定 memoryTypes[memoryTypeIndex] 对应的内存类型上进行内存分配,对应分配的堆为 memoryHeaps[memoryTypes[memoryTypeIndex].heapIndex]

参考代码如下:

void vulkanBasicDemo::vulkangetMemoryInfo() {VkDeviceSize size = 1024;VkDeviceMemory memory;// 获取物理设备内存属性VkPhysicalDeviceMemoryProperties memoryProperties;vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);std::cout << "=============================================================" << std::endl;// 查找一个主机可见的内存类型uint32_t memoryTypeIndex = VK_MAX_MEMORY_TYPES;for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) {if ((memoryProperties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) &&(memoryProperties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {memoryTypeIndex = i;break;}}std::cout << "get memoryTypeIndex:" << memoryTypeIndex << std::endl;if (memoryTypeIndex == VK_MAX_MEMORY_TYPES) {std::cerr << "failed!! Could not find a suitable memory type!" << std::endl;;}// 准备内存分配信息VkMemoryAllocateInfo allocInfo = {};allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;allocInfo.allocationSize = size;allocInfo.memoryTypeIndex = memoryTypeIndex;// 分配内存VkResult result = vkAllocateMemory(device, &allocInfo, nullptr, &memory);if(result == VK_SUCCESS) {std::cout << "Memory allocated successfully!" << std::endl;}
}
内存映射

使用vkMapMemory实现宿主机队设备内存的映射访问,这个函数会返回一个虚拟地址的指针,指向映射后的设备内存区域
如果内存分配时指定的内存类型支持 VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,说明该内存是可以映射的
原则上并不是所有的设备内存都可以从主机端进行读写,为了 CPU 能够读写设备内存,硬件供应商都会提供一部分带有 VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT属性的内存用于CPU访问
Vulkan中分配的内存会得到一个VkDeviceMemory对象,通过vkMapMemory()函数将分配的设备内存底层的虚拟地址返回给CPU(也就是Host端)
vkMapMemory 的函数原型:

VKAPI_ATTR VkResult VKAPI_CALL vkMapMemory(VkDevice                                    device,VkDeviceMemory                              memory,VkDeviceSize                                offset,//从内存首地址开始的偏移量,从 0 开始VkDeviceSize                                size,  // 映射的大小,单位为字节VkMemoryMapFlags                            flags,void**                                      ppData // 产生的虚拟地址为 void* 指针);

需要注意的是,映射的 memory 必须在有VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT 类型的内存上分配
当函数成功返回后,memory 就会在 Host 端进行了内存映射,并处于映射态
当内存映射完成并使用结束后,可以进行接触映射的操作,进而释放系统的虚拟内存,可以通过 vkUnmapMemory() 函数将映射过的内存进行解映射

参考代码如下:

.....
// 分配内存
VkResult result = vkAllocateMemory(device, &allocInfo, nullptr, &memory);
if(result == VK_SUCCESS) {std::cout << "Memory allocated successfully!" << std::endl;
}// 映射内存
void* data;
result = vkMapMemory(device, memory, 0, size, 0, &data);
if (result == VK_SUCCESS) {std::cout << "Memory mapped successfully!" << std::endl;
}// 写入数据到内存
uint32_t* intData = (uint32_t*)data;
for (uint32_t i = 0; i < size / sizeof(uint32_t); i++) {intData[i] = i;
}
std::cout << "Data written to memory successfully!" << std::endl;// 解除内存映射
vkUnmapMemory(device, memory);
std::cout << "Memory unmapped successfully!" << std::endl;vkFreeMemory(device, memory, nullptr);

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 树上启发式合并——dsu on tree
  • React学习笔记(三)——redux状态管理工具
  • mysql的安装与初始化
  • Python--正则表达式
  • Centos7整合fail2ban配置ssh和pgsql以及vault
  • 开发日记-EaxyExcel修改模板sheet名称
  • 如何实现一棵红黑树
  • Element-UI自学实践(二)
  • 【Node】【4】事件循环和EventEmitter类
  • 2024年特种设备作业人员考试题库及答案(流动式起重机Q2)
  • 查找数学类文献的专业数据库有哪些 如何获取这些数据库资源
  • Blazor官方文档学习记录
  • 2024.8.24
  • iPhone抹掉数据后能恢复吗?详解数据恢复的可能性与方法
  • 面向对象02:构造器详解
  • [iOS]Core Data浅析一 -- 启用Core Data
  • canvas 五子棋游戏
  • emacs初体验
  • Java新版本的开发已正式进入轨道,版本号18.3
  • node-glob通配符
  • React16时代,该用什么姿势写 React ?
  • React-flux杂记
  • REST架构的思考
  • Vim Clutch | 面向脚踏板编程……
  • 测试如何在敏捷团队中工作?
  • 动手做个聊天室,前端工程师百无聊赖的人生
  • 免费小说阅读小程序
  • 日剧·日综资源集合(建议收藏)
  • 入口文件开始,分析Vue源码实现
  • 使用SAX解析XML
  • 思维导图—你不知道的JavaScript中卷
  • 终端用户监控:真实用户监控还是模拟监控?
  • Oracle Portal 11g Diagnostics using Remote Diagnostic Agent (RDA) [ID 1059805.
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • ​卜东波研究员:高观点下的少儿计算思维
  • ​七周四次课(5月9日)iptables filter表案例、iptables nat表应用
  • # 计算机视觉入门
  • ### Error querying database. Cause: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
  • ### RabbitMQ五种工作模式:
  • ###项目技术发展史
  • (Bean工厂的后处理器入门)学习Spring的第七天
  • (BFS)hdoj2377-Bus Pass
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (翻译)Entity Framework技巧系列之七 - Tip 26 – 28
  • (附源码)spring boot基于Java的电影院售票与管理系统毕业设计 011449
  • (附源码)springboot车辆管理系统 毕业设计 031034
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • (一)SpringBoot3---尚硅谷总结
  • (译)计算距离、方位和更多经纬度之间的点
  • (自适应手机端)行业协会机构网站模板
  • (自用)仿写程序
  • *ST京蓝入股力合节能 着力绿色智慧城市服务
  • .NET Core 将实体类转换为 SQL(ORM 映射)
  • .NET Framework Client Profile - a Subset of the .NET Framework Redistribution