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

news2024/9/19 15:19:57

目录

        • 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);

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

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

相关文章

Android自定义简单仿QQ运动步数进展圆环

实现效果主要效果分为三个部分&#xff1a; 1.固定蓝色的大圆弧 color borderWidth 2.可以变化的小圆弧(红色) color borderWidth 3.中间的步数文字 color textSize drawArc方法 startAngle 确定角度的起始位置 sweepAngle 确定扫过的角度 useCenter 是否使用中心&#xff1a…

MyBatis XML配置文件(下)

MyBatis的开发有两种方式&#xff1a;1、注解 2、XML。使用MyBatis的注解方式&#xff0c;主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能&#xff0c;建议使用XML来配置映射语句&#xff0c;也就是将SQL语句写在XML配置文件中。 MyBatis XML开发的方式需要以…

UE5学习笔记17-让人物的视线和鼠标移动时的方向一致,并且不让人物模型旋转,只改变视线方向

一、创建标准动画帧 1.我想让人物在装备武器后根据鼠标的移动方向改变人物的视线方向&#xff0c;并且人物模型不会改变朝向 2.我的动画中存在一个四个方向瞄准的动画&#xff0c;将左下&#xff0c;坐上&#xff0c;左转&#xff0c;右上&#xff0c;右下&#xff0c;右转&…

C++ 设计模式——组合模式

C 设计模式——组合模式 C 设计模式——组合模式1. 主要组成成分2. 逐步构建透明组合模式1. 定义抽象组件&#xff08;Component&#xff09;2. 实现叶子组件&#xff08;Leaf&#xff09;3. 实现组合组件&#xff08;Composite&#xff09;4. 主函数&#xff08;Main&#xff…

Nacos踩坑

最近遇到项目部署&#xff0c;遇到Nacos中的配置读取不到&#xff0c;导致服务起不来。服务器银河麒麟x86&#xff0c;Nacos版本2.3.2, openJdk8u43 报错如下&#xff1a; java.lang.UnsatisfiedLinkError: no com_alibaba_nacos_shaded_io_grpc_netty_shaded_netty_transport…

Linux磁盘操作之du命令

使用du命令&#xff0c;您可以查看指定目录或文件的磁盘使用量总计。这对于了解特定目录或文件占用的磁盘空间大小非常有用&#xff0c;可以帮助您进行磁盘空间管理和清理。 参数说明 du是一个用于显示目录或文件的磁盘使用情况的命令&#xff0c;du是disk usage的缩写&#…

从图像到视频:Web Codecs API编码技术解析

初探Web Codecs API 三 前言 在之前的文章中,咱们简单的介绍了解码相关的东西,这一节咱们来简单聊聊编码相关的东西。 编码的目的就是为了压缩,去除空间、时间维度的冗余。 这里又不得不提起前面所说的I 帧、P 帧、B 帧和 IDR 帧。 众所周知,视频是连续的图像序列,由…

【添加核心机械臂动力学】Model and Control a Manipulator Arm with Robotics and Simscape

机械臂动力学 为了设计控制器&#xff0c;机械臂动力学必须表示给定力矩输入的关节位置。这在机械臂动力学子系统中通过一个前向动力学块实现&#xff0c;该块将关节力矩转换为给定当前状态的关节加速度&#xff0c;然后通过两次积分得到完整的关节配置。积分器初始化为q0和dq…

自闭症托管托养机构:星贝育园的优势与使命

在当今社会&#xff0c;自闭症儿童作为一群需要特别关注和照顾的群体&#xff0c;其教育与康复问题日益受到社会各界的重视。自闭症托管托养机构作为这一领域的重要力量&#xff0c;承担着为自闭症儿童提供全方位、个性化支持的重任。星贝育园&#xff0c;作为一所全日寄宿制的…

使用libsvm时遇到MATLAB has encountered an internal problem and needs to close

最近在MATLAB中使用libsvm跑别人的程序&#xff0c;该程序在大部分数据集上可以正常运行&#xff0c;但在有一个数据集上运行时MATLAB会报“MATLAB has encountered an internal problem and needs to close”的错误&#xff1a; 凭直觉猜应该是数据集有啥问题&#xff0c;但又…

AI人的列表!《中国人工智能学会推荐国际学术会议和国际/国内期刊目录》正式发布

在全球科技迅猛发展的今天&#xff0c;人工智能&#xff08;AI&#xff09;已成为推动社会进步的关键力量。为了更好地指导AI领域的科研方向&#xff0c;加强学术交流&#xff0c;促进学术成果的创新与应用&#xff0c;中国人工智能学会&#xff08;CAAI&#xff09;在2024年8月…

数据分析案例-2024年裁员数据集可视化分析

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

XFTP软件的使用 ---- 远程上传、下载文件

本文假设你的电脑里面已经有XFTP软件。 一、简介 是一个基于 windows 平台的功能强大的SFTP、FTP文件传输软件。通过Xftp软件&#xff0c;windows 用户能安全地在UNIX/Linux 和 Windows PC 之间传输文件。 二、 使用方法【步骤】 打开软件&#xff0c;得到如下图界面。 首先我…

STL--unordered_set和unordered_map的模拟实现

1.unordered系列关联式容器 在C98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时效率可达到&#xff0c;即最差情况下需要比较红黑树的高度次&#xff0c;当树中的节点非常多时&#xff0c;查询效率也不理想。最好的查询是&#xff0c;进行很…

如何使用ssm实现基于java斗车交易系统设计与实现+vue

TOC ssm082基于java斗车交易系统设计与实现vue 系统概述 1.1 概述 随着社会的快速发展&#xff0c;计算机的影响是全面且深入的。人们的生活水平不断提高&#xff0c;日常生活中人们对斗车交易方面的要求也在不断提高&#xff0c;需要咨询的人数更是不断增加&#xff0c;使得…

OpenCV 图像处理基础算法介绍c++

VS2022配置OpenCV环境 关于OpenCV在VS2022上配置的教程可以参考&#xff1a;VS2022 配置OpenCV开发环境详细教程 图像处理 图像处理是一个广泛的领域&#xff0c;它涉及到对图像数据进行分析、修改和改进的各种技术。以下是一些基本的图像处理操作&#xff0c;这些操作通常可…

模型 蒂蒙斯创业过程

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。机会、团队、资源&#xff1a;创业成功的三角。 1 蒂蒙斯创业过程的应用 1.1 京东&#xff1a;蒂蒙斯模型下创业成功的典范 京东是中国知名的电子商务平台&#xff0c;其早期发展阶段充分体现了蒂蒙…

接口基础知识12:cookie、session和token

​课程大纲 ‌Cookie、Session和Token是Web应用中常用的身份验证和会话管理机制&#xff0c;各有特点&#xff0c;适用于不同的应用场景。 一、Cookie 1.1 什么是Cookie Cookie是存储在客户端浏览器中的小段数据&#xff0c;通常用于存储用户偏好设置或用于跟踪用户活动。 C…

51单片机——数码管控制

1、数码管介绍 LED数码管&#xff1a;数码管是一种简单、廉价的显示器&#xff0c;是由多个发光二极管封装在一起组成“8”字型的器件。 2、数码管驱动方式 单片机直接扫描&#xff1a;硬件设备简单&#xff0c;但会耗费大量的单片机CPU时间 专用驱动芯片&#xff1a;内部自…

8月24日笔记

ew的使用 EW&#xff08;EarthWorm&#xff09;是一套基于C语言开发的轻量级且功能强大的网络穿透工具&#xff0c;它支持Socks5代理、端口转发及端口映射等功能&#xff0c;能够在复杂的网络环境中实现高效稳定的网络穿透。这款全平台适用的工具&#xff08;包括Windows、Lin…