从物理硬件获取内存类型索引
- GPU里有存储纹理的内存、存储通用数据的内存。
- 当申请一个vkBuffer的时候,它只是定义了一个逻辑上的对象,真正需要开辟物理内存时,需要根据要存储的是对象类型来决定给与它什么样的内存。
- 获取类型有两个条件
- 32位掩码:来自VkMemoryRequirements
- VkMemoryPropertyFlags:这个设置代表着内存的访问权限,是GPU访问还是CPU访问。当CPU可见时,GPU访问会很慢。
1 | uint32_t xGetMemoryType(uint32_t type_filters, VkMemoryPropertyFlags properties) |
从GPU获取一块内存
流程
- 申请一个buffer
- 定义VkBufferCreateInfo,给出size
- 定义VkBuffer对象:用于管理一块GPU物理内存的逻辑对象
- 调用vkCreateBuffer
- 获取这个buffer的内存请求信息
- 定义VkMemoryRequirements:GPU上的物理内存是被分成各种类型的,存储纹理的,存储VBO的
- 调用vkGetBufferMemoryRequirements获取info
- 获取分配器信息
- 定义VkMemoryAllocateInfo
- 设置allocationSize和memoryTypeIndex
- 定义VkDeviceMemory对象:代表是一块GPU上的真实内存
- 调用vkAllocateMemory获取内存
- 调用vkBindBufferMemory把VkBuffer对象和VkDeviceMemory对象绑定
代码
- 其中FindMemoryType后续补完
1 | VkResult xGenBuffer(VkBuffer &buffer, VkDeviceMemory &buffermemory, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties) |
向GPU写入数据
需要知道的点
- Vulkan把memory分成两种,一种是hostmemory,就是CUP可见的内存区域;第二种是device memory,也就是显存。当显卡没有显存时,需要使用内存给它当显存用,通常嵌入式设备上就是这么干的,还比如集成显卡。
- 在使用Vulkan API的时候会发现很多地方都用到一个参数VkAllocationCallbacks,这个回调的用途是让程序员有机会去自己分配host memory。如果传空,Vulkan就会调用它自己实现的分配host memory的逻辑。通常,设置这个回调是为了debug,监测内存的使用情况,调试程序。
- CPU无法直接向显存写入数据,它只能将数据写入到显存外的一块CPU和GPU都可见、可读取的内存中,然后发图形命令让GPU去这块内存copy数据到显存。
流程
将数据写到一块不是显存的内存块中
创建一块Vulkan管理的上的buffer和memory,但是这块内存不在显存,因为显卡会拿一部分内存自己用,这就是个中转区域,传递的时候用DMA控制器,不需要耗费cpu的时钟。
将一个VkDeviceMemory映射到一个实际的写入物理地址,这地址是应用程序可以识别或者说可以使用的。参考:vkMapMemory(3)
写出数据
解除绑定
使用commandbuffer写入GPU
- 创建commandbuffer
- 调用vkCmdCopyBuffer让GPU来copy内存
- 清理临时数据
1 | // 拷贝数据从CPU到GPU |
总结:
- 到此,完成了CPU数据到GPU数据写入的过程,后两段代码分别对标了OpenGL中的glGenBuffers和glBufferSubData。
- 一个buffer是干什么用的,它对应的物理内存的位置是哪里,由VkBufferUsageFlags和VkMemoryPropertyFlags决定。
- vkCmdCopyBuffer是可以任意拷贝的,从CUP到GPU,从GPU到GPU,从GPU到CUP。只看参数的设置。从这个API可以看到Vulkan的高自由度。
- 学习Vulkan,可以看到OpenGL背后的细节