Vulkan内存模型+管理

前言

最近在研究Vulkan,在Vulkan中使用内存是个麻烦的过程,而且容易用错,今天就给大家分享下Vulkan的内存模型。

内存,在任何时候都是个稀缺的资源,内存管理更是个让人望而却步的事情。在这个崇尚用户体验的今天,不管是底层系统还是上层应用都在追求极致的性能优化,内存优化也是重中之重。从系统层的分页,池化,脏页回收,连续大内存到上层的优化的数据结构,内存共享,压缩等都在不停地压榨系统内存以获得更高性能的应用程序。

Vulkan内存初心

我认为简单地理解Vulkan区别于OpenGL的最大特点就是Vulkan可以让应用开发者细粒度的控制,在内存方面也是如此。因为Vulkan认为应用自身对于内存占用是最清楚的,内存的创建释放,生命周期都是应用自身的行为导致的。面对复杂的应用场景,很难有通用的优化策略解决所有问题。不管是通过虚拟机还是驱动程序,帮助应用进行内存管理永远是低效的。

基于上述原因,Vulkan将内存管理的工作交给了开发者负责,如何分配释放内存,怎样制定内存策略都由开发者自己决定,这无疑是返璞归真的至理。但话说回来这样的机制对开发者来说却不是友好的,所以我们更需要知道Vulkan的内存模型才能更高效地管理。

Vulkan内存管理

Vulkan中的内存分为两种:宿主内存和设备内存。

这两种内存的特点是宿主内存比设备内存慢。但是宿主内存的容量通常更大。另一方面来说,设备内存是直接对物理设备可见的,因此它更有效率也更为快速。

宿主内存

Vulkan使用宿主内存来存储API的内部数据结构。Vulkan提供了内存分配器机制,允许应用程序控制宿主机端的内存分配。如果应用程序不使用分配器机制,那么Vulkan将使用一个默认的分配器来管理内存和数据结构。

主机内存管理通过以下数据结构来完成:

typedef struct VkAllocationCallbacks {Void*                                 pUserData;PFN_vkAllocationFunction              pfnAllocation;PFN_vkReallocationFunction            pfnReallocation;PFN_vkFreeFunction                    pfnFree;PFN_vkInternalAllocationNotification  pfnInternalAlloc;PFN_vkInternalFreeNotification        pfnInternalFree;} VkAllocationCallback
  • pUserData是由用户自定义的值,因为每次回调的时候这个值可能会变。

  • PFN_vkAllocationFunction是一个指向应用程序定义的内存分配函数的指针,用来管理Vulkan API创建的数据结构产生的内存。

  • PFN_vkReallocationFunction是一个指向应用程序定义的内存重分配函数的指针,用来重新管理Vulkan API创建的数据结构产生的内存。

  • PFN_vkFreeFunction是一个指向应用程序定义的内存释放函数。

  • PFN_vkInternalAllocationNotification是一个指向应用程序定义的函数的指针,当被Vulkan实现调用时,就会给应用程序发内存分配的通知

  • PFN_vkInternalFreeNotification是一个指向应用程序定义的函数的指针,当被Vulkan实现调用时,就会给应用程序发释放内存的通知

Vulkan对宿主内存的要求就是内存地址是对齐的,这是因为某些高性能CPU指令在对齐的内存地址上效果最佳。通过假定存储CPU端数据结构的分配是对齐的,Vulkan可以无条件使用这些高性能指令,从而提供显著的性能优势。

设备内存

设备内存, 即GPU内存,它对于物理设备是直接可见的, 物理设备可以直接读取其中的内存区块。图像对象,缓存对象以及UBO(uniform buffer objec)都是在设备内存端分配的。

用vkGetPhysicalDeviceMemoryProperties函数查询后可以得到一个VkPhysicalDeviceMemoryProperties结构体中记载了物理设备上的内存属性。

void vkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice                       physicalDevice,VkPhysicalDeviceMemoryProperties*      pMemoryProperties)typedef struct VkPhysicalDeviceMemoryProperties { Uint32_t         memoryTypeCount;VkMemoryType     memoryTypes[VK_MAX_MEMORY_TYPES];Uint32_t         memoryHeapCount;VkMemoryHeap     memoryHeaps[VK_MAX_MEMORY_HEAPS];} VkPhysicalDeviceMemoryProperties

内存分配

VkResult vkAllocateMemory (    VkDevice                       device;const VkMemoryAllocateInfo*    allocateInfo,const VkAllocationCallbacks*   allocator,    VkDeviceMemory*                memory)typedef struct VkMemoryAllocateInfo {  VkStructureType  type;    Const void*      pNext;    VkDeviceSize     allocationSize;uint32_t         momoryTypeIndex;        // 内存类型索引} VkMemoryAllocateInfo
  • 用vkAllocateMemory函数来分配VkDeviceMemory类型的设备内存对象。

  • type代表类型,类型必须是VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO.

  • allocationSize指定了分配的内存大小(字节)

  • memoryTypeIndex的值就可以用刚刚的辅助函数获取。

  • pNext扩展指针可以填入一个VkMemoryDedicatedAllocateInfo结构体,这样就可以在分配内存的时候指定一个专用Buffer或Image对象.

释放内存

Void VkFreeMemory (VkDevice          device,  VkDeviceMemory    memory,  const VkAllocationCallbacks* allocator)
  • VKDevice设备句柄

  • VKDeviceMemory 准备释放的内存对象

  • allocator 控制内存释放的分配器对象

内存和资源

解决了内存分配的问题,但GPU绘制过程中需要各种资源,而资源通常是存储在CPU内存中的,和GPU内存并不互通,无法被GPU直接访问,因此我们需要一个方法把资源放到GPU内存中而且能被GPU按照一定的规矩访问。

Buffer and Image

Buffer是最简单的资源类型,可以用来储存线性的结构化的数据,也可以储存内存中原始字节。它可以通过调用命令缓冲区来绑定,交由GPU硬件操作。Vulkan中用VkBuffer句柄来指示Buffer对象,并且用以下方法进行创建:

typedef struct VkBufferCreateInfo {VkStructureType sType;const void* pNext;VkBufferCreateFlags flags;VkDeviceSize size;VkBufferUsageFlags usage;VkSharingMode sharingMode;uint32_t queueFamilyIndexCount;const uint32_t* pQueueFamilyIndices;} VkBufferCreateInfo;
VkResult vkCreateBuffer(VkDevice device,const VkBufferCreateInfo* pCreateInfo,const VkAllocationCallbacks* pAllocator,VkBuffer* pBuffer );
  • flags是一个VkBufferCreateFlagBits类型的枚举

  • size是VkBuffer映射的一段区域的内存大小,即数据大小。

  • usage是Buffer的具体功用,例如用作顶点缓存,索引缓存,转移缓存

  • sharingMode明确了Buffer被多个队列共享访问的模式,一般选VK_SHARING_MODE_EXCLUSIVE。

  • queueFamilyIndexCount和pQueueFamilyIndices代表访问这个Buffer的队列族

  • pNext在Vulkan1.1版本后,允许我们使用VkExternalMemoryBufferCreateInfo结构体来创建一个用于存储的外部缓冲,在Vulkan1.2版本后,允许我们使用VkBufferOpaqueCaptureAddressCreateInfo结构体来为Buffer要求具体的设备地址。

Image相对复杂,其具有特殊的布局和格式。Image的布局(layout)对内存有特殊需求,主要有两种主要的平铺模式:

  • linear - 其中的图像(Image)数据线性排列在内存中。

  • optimal - 其中的图像(Image)数据以高度优化的模式进行布局,可以有效利用设备的内存子系统。

    线性布局(linear layout)适合连续的单行的读写,但是大多数图形操作都涉及到跨行读写纹理元素,如果图像自身的宽度非常宽,相邻行的访问在线性布局中会有非常大的跳转。这可能会导致性能问题。

优化布局(optimal layout)的好处是内存数据根据不同内存子系统进行优化,比如将所有的纹理像素都优化到一块连续的内存区域中,加快内存处理速度。下图很形象地说明了两种布局的优劣势:

GPU通常倾向于使用优化布局以实现更有效的渲染。但优化部分因不同品牌有差异且是内部逻辑,所以CPU想要读取图像信息还需要多一层转换。

总结

本文讲述了Vulkan的内存布局和管理方式,希望能对大家后续实现Vulkan程序有所帮助,在此建议大家尽量做到内存复用,因内存分配和释放都需要昂贵的开销。连续内存对象可以享受更好的缓存利用率,内存对齐的数据性能更优。

微信公众号首发,欢迎关注:江湖修行,欢迎关注,转发,评论交流

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/350519.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

自学测试半年,终于收到了字节的offer,那一刻我哭出了声...

我是一名毕业于普通一本的化学专业学生,毕业的两年时间里,我一直奔波在化工厂里。每天工作三班倒,下了班就是一包烟一瓶酒,生活过得非常堕落。 原本想着虽然每天很累,但是至少稳定。然而没有想到的是,化工…

注册表启动项设置方法

1、按winr,打开运行输入框,输入regedit,打开注册表。 2、在注册表输入框中复制下面路径: \HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run 3、新增一条开机启动项 右键 -新建-字符串值,修改启动项…

计算机启动项在什么地方找,W7系统注册表开机启动项在哪里

开机启动项除了可以使用MSConfig命令打开以外,也能使用第三方工具、注册表查看开机启动项,因为数值的存在,注册表看到的启动项比较准确。为了个人信息安全,使用注册表编辑开机启动项以更好的杜绝病毒自启动。阅读下文了解w7系统注…

WIN10 注册表添加启动项

1、打开运行,输入“regedit”,打开注册表。 2、在注册表中找到如下位置\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run,右键“Run”新建一个字符串类型的键值。 3、右键新建的键值,选择“修改”,将数值名称…

WINDOW 注册表添加启动项

注册表添加启动项 1、打开运行,输入“regedit”,打开注册表。 2、在注册表中找到如下位置HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run,winwin7.com右键“Run”新建一个字符串类型的键值。 3、右键新建的键值&…

修改注册表开机启动项

首先我们启动项的位置,它有两个一个是user一个是michine我们先来看HKEY_CURRENT_USER下面的,打开注册表方式非常简单笔记本winR台式机可以直接点击开始运行输入regedit 找到注册表之后依次展开HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVe…

win10添加开机启动项_Windows系统注册表添加开机启动项

Windows 7系统我们可以把需要开机启动的软件把快捷键放到(开始/程序/启动)就可以了,而Windows 10系统没有(开始/程序/启动)启动项,我们要添加软件开机自动启动就需要到注册表添加修改,而注册表里面添加启动项有两个类型, 1、[HKEY…

屏蔽360阻止远程执行变更注册表自启动数据的办法

屏蔽360阻止远程执行变更注册表自启动数据的办法 运程服务器上的程序,由于需要。我在服务器中,加入更新升级自身(exe)文件,并变更操作系统自启动数据的代码。 实践证明,通过客户端,调用运程服务…

实验一——病毒注册表操作

【实验内容】 (1)强制隐藏.exe文件的扩展名 刚开始应用程序的exe扩展名都是可见的 1、注册表项:HKEY_CLASS_ROOT\exefile, 2、新建字符串值:取名为NeverShowExt。 用Mytool工具: 直接在注册表中添加 …

win7系统注册表开机启动项如何打开?

在运行窗口输入MSConfig命令不仅可以打开开机启动项,也能使用第三方工具、注册表查看开机启动项,注册表看到的启动项比较准确。而且使用注册表编辑开机启动项以更好的杜绝病毒自启动。一些win7的用户反馈说不知道win7系统注册表开机启动项如何打开&#…

揭秘广告投放的9大关键环节,了解真相让你成为广告投放高手!

正式开始本章的内容之前,先来简单复习一下上一章的主要内容: 核心要点1:广告投放的意义主要有三点:传播品牌、宣传产品、促成转化; 核心要点2:广告投放的主要流程有这样 9 个阶段: 本章我们以…

(栈和队列) 1047. 删除字符串中的所有相邻重复项 ——【Leetcode每日一题】

❓1047. 删除字符串中的所有相邻重复项 难度:简单 给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。 在 S 上反复执行重复项删除操作,直到无法继续删除。 在完成所有重复项删除操作后返回…

【阿里内部教程】python初阶:基础语法 python全栈自动化测试系类

目录 很多小伙伴可能都没有看过凡哥的视频,所以大家可能对凡哥不是很了解这里先和大家来个自我介绍 凡哥我已经有着十二年互联网自动化测试和测试开发工程师,拥有丰富的自动化测试平台及测试开发经验,擅长接口测试、Python自动化全栈&#x…

前端面试题——Vue 高频

目录 一、Vue的基本原理 二、双向数据绑定的原理 三、MVVM、MVC、MVP的区别 四、Computed 和 Watch 的区别 五、Computed 和 Methods 的区别 六、v-if 和 v-show的区别 七、data为什么是一个函数而不是对象 八、Vue 单页应用与多页应用的区别 九、对 React 和 Vue 的理…

今天面了个字节跳动拿30k出来的测试大佬,让我见识到了什么是天花板

2022年堪称大学生就业最难的一年,应届毕业生人数是1076万。失业率超50%! 但是我观察到一个数据,那就是已经就业的毕业生中,计算机通信等行业最受毕业生欢迎! 计算机IT行业薪资高,平均薪资是文科其他岗位的…

Proxifier+secureCRT建立隧道

一、Proxifier新建规则 1、新建服务 这里端口自己随便填。 2、新建规则 二、secureCRT设置端口转发 注意,这里端口选择与proxifier设置的端口一致 初步理解是:本地无法直接访问上海数据库,但是可以通过secureCRT连接的跳板机进行访问。该设…

Proxifier实现抓包

Proxifier实现抓包 通过Proxifier代理实现抓包,如微信小程序 burpProxifier微信小程序 burp设置: 微信小程序: 1.查看任务管理器 2.找到已打开的小程序名,右键打开该程序的路径(选择打开文件所在的位置&#xff09…

proxifier软件最新安装教程

目录 一、官网地址 二、软件介绍 三、软件下载 四、安装步骤 一、官网地址 官网地址:https://www.proxifier.com/ 二、软件介绍 Proxifier是一款功能非常强大的socks5客户端,可以让不支持通过代理服务器工作的网络程序能通过HTTPS或SOCKS代理或代理…

Proxifier安装与使用

Proxifier安装与使用 1.Proxifier官网可能打不开,这是一个下载地址,提取码为p1l8。 用户名随意填 注册码下边 5EZ8G-C3WL5-B56YG-SCXM9-6QZAP G3ZC7-7YGPY-FZD3A-FMNF9-ENTJB YTZGN-FYT53-J253L-ZQZS4-YLBN9 三选一 2.安装完毕后,在Profier菜…

Proxifier使用HTTP全局代理配置教程

一、安装Proxifier软件在电脑上 二、打开Proxifier软件 点击配置稳文件——代理服务器 以华科HTTP代理为例 例如:(地址:ip5.hahado.cn:37744 用户名:142366 密码:142366 ) 点击检查代理是否正常 设…